Vincular un valor de objetos dentro de una función (cierre)

En SML (un lenguaje de progtwigción funcional que aprendí antes de Python), puedo hacer lo siguiente:

val x = 3; fun f() = x; f(); >>> 3 val x = 7; f(); >>> 3 

En Python, sin embargo, la primera llamada dará 3 y la segunda dará 7.

 x = 3 def f(): return x f() >>> 3 x = 7 f() >>> 7 

¿Cómo puedo vincular el valor de una variable a una función en Python?

Puede utilizar un argumento de palabra clave:

 x = 3 def f( x=x ): return x x = 7 f() # 3 

Los argumentos de palabra clave se asignan cuando se crea la función . Otras variables se buscan en el scope de la función cuando se ejecuta la función . (Si no se encuentran en el scope de la función, python busca la variable en el scope que contiene la función, etc., etc.).

Podrías hacerlo:

  x = 3 f = lambda y=x: y f() >>> 3 x = 7 f() >>> 3 

Como mgilson y Neal, la forma más directa de hacerlo es utilizar un argumento predeterminado cuando definas f .

Un tema más importante es el desajuste filosófico aquí. Python realiza cierres, pero funcionarán de forma diferente a lo que usted espera de SML (con el que no estoy muy familiarizado) o Haskell o Clojure o algo parecido (ambos con los que estoy más familiarizado). Esto se debe a la forma en que Python maneja los cierres, pero también a la forma en que define las funciones anónimas de manera diferente y permite efectos secundarios.

Normalmente, cuando los enfoques funcionales no funcionan bien, en Python usas una clase, en este caso para capturar el valor de x.

 class F(object): def __init__(self, x): self.x = x def __call__(self): return self.x x = 3 f = F(x) f() # 3 x = 7 f() # 3 

Esta es una manera, demasiado para este ejemplo, y obviamente no lo recomendaría aquí. Pero a menudo en Python, la respuesta es usar una clase. ( Eso es una statement en exceso bruta, y las excepciones de mi cerebro se generan más rápido de lo que puedo escribirlas. Pero cuando viene de un fondo funcional, creo que esta statement es una buena heurística cuando está aprendiendo a abordar los problemas pythonicamente. . )

Además, también es interesante que esto hace explícito lo que hace implícitamente la captura de la variable en la definición del argumento de la función: crea un objeto de función que se engancha en el valor de x en el punto en que se crea el objeto.

Esta pregunta parece ser sobre cómo capturar el valor de (no una referencia a) una variable externa en el momento en que se crea un cierre. Y en ese sentido las respuestas habituales son usar argumentos por defecto:

 def f(x=x): return x # or f = lambda x=x: x 

(sin embargo, esto no funcionará si desea una función que tome un número variable de argumentos). O más generalmente (funciona incluso en el caso de argumentos variables) pero menos bonito, utilizando una función de ajuste explícita que se ejecuta de inmediato:

 f = (lambda x: lambda: x)(x) 

Sin embargo, solo quería señalar que la premisa de su pregunta no es válida : en SML, no se puede asignar a una variable, nunca (no estoy hablando de modificar una estructura de datos mutable como ref ; estoy hablando de asignación a variables); ni siquiera hay syntax en el lenguaje para hacer esto.

Lo que está haciendo en su código SML es crear un nuevo ámbito y definir una variable diferente con el mismo nombre x en el nuevo ámbito, que oculta la x anterior. Siempre puede simplemente cambiar el nombre de la segunda variable a un nombre diferente y no tener ningún efecto en el progtwig. Ahora, Python solo tiene un scope de función, y no tiene ámbitos internos para bloques de código dentro de una función. Pero puede aplicar la misma solución: solo cambie la segunda variable por un nombre diferente y no tendrá este problema.