Modificar variables encuadernadas de un cierre en Python

¿Hay alguna forma de modificar el valor límite de una de las variables dentro de un cierre? Mira el ejemplo para entenderlo mejor.

def foo(): var_a = 2 var_b = 3 def _closure(x): return var_a + var_b + x return _closure localClosure = foo() # Local closure is now "return 2 + 3 + x" a = localClosure(1) # 2 + 3 + 1 == 6 # DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0 # ...but what magic? Is this even possible? # Local closure is now "return 0 + 3 + x" b = localClosure(1) # 0 + 3 +1 == 4 

No creo que haya ninguna manera de hacer eso en Python. Cuando se define el cierre, se captura el estado actual de las variables en el ámbito de cierre y ya no tiene un nombre directamente referenciable (desde fuera del cierre). Si volviera a llamar a foo() , el nuevo cierre tendría un conjunto diferente de variables del ámbito que lo incluye.

En su ejemplo simple, podría estar mejor usando una clase:

 class foo: def __init__(self): self.var_a = 2 self.var_b = 3 def __call__(self, x): return self.var_a + self.var_b + x localClosure = foo() # Local closure is now "return 2 + 3 + x" a = localClosure(1) # 2 + 3 + 1 == 6 # DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0 # ...but what magic? Is this even possible? localClosure.var_a = 0 # Local closure is now "return 0 + 3 + x" b = localClosure(1) # 0 + 3 +1 == 4 

Si usas esta técnica, ya no usaría el nombre localClosure porque ya no es realmente un cierre. Sin embargo, funciona igual que uno.

Es bastante posible en python 3 gracias a la magia de nonlocal .

 def foo(): var_a = 2 var_b = 3 def _closure(x, magic = None): nonlocal var_a if magic is not None: var_a = magic return var_a + var_b + x return _closure localClosure = foo() # Local closure is now "return 2 + 3 + x" a = localClosure(1) # 2 + 3 + 1 == 6 print(a) # DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0 localClosure(0, 0) # Local closure is now "return 0 + 3 + x" b = localClosure(1) # 0 + 3 +1 == 4 print(b) 

He encontrado una respuesta alternativa a la respuesta de Greg, un poco menos detallada porque utiliza los atributos de función personalizados de Python 2.1 (a los que se puede acceder desde su propia función).

 def foo(): var_b = 3 def _closure(x): return _closure.var_a + var_b + x _closure.func_dict['var_a'] = 2 return _closure localClosure = foo() # Local closure is now "return 2 + 3 + x" a = localClosure(1) # 2 + 3 + 1 == 6 # DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0 # ...but what magic? Is this even possible? # apparently, it is localClosure.var_a = 0 # Local closure is now "return 0 + 3 + x" b = localClosure(1) # 0 + 3 +1 == 4 

Pensé que lo publicaría para completar. Saludos de todos modos.

Hemos hecho lo siguiente. Creo que es más simple que otras soluciones aquí.

 class State: pass def foo(): st = State() st.var_a = 2 st.var_b = 3 def _closure(x): return st.var_a + st.var_b + x def _set_a(a): st.var_a = a return _closure, _set_a localClosure, localSetA = foo() # Local closure is now "return 2 + 3 + x" a = localClosure(1) # 2 + 3 + 1 == 6 # DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0 localSetA(0) # Local closure is now "return 0 + 3 + x" b = localClosure(1) # 0 + 3 +1 == 4 print a, b 

Resolví una limitación similar utilizando listas de un solo elemento en lugar de una variable simple. Es feo, pero funciona porque la modificación de un elemento de la lista no se trata como una operación de enlace por parte del intérprete.

Por ejemplo:

 def my_function() max_value = [0] def callback (data) if (data.val > max_value[0]): max_value[0] = data.val # more code here # . . . results = some_function (callback) store_max (max_value[0]) 

¿Por qué no hacer los argumentos var_a y var_b de la función foo?

 def foo(var_a = 2, var_b = 3): def _closure(x): return var_a + var_b + x return _closure localClosure = foo() # uses default arguments 2, 3 print localClosure(1) # 2 + 3 + 1 = 6 localClosure = foo(0, 3) print localClosure(1) # 0 + 3 + 1 = 4 
 def foo(): var_a = 2 var_b = 3 def _closure(x): return var_a + var_b + x return _closure def bar(): var_a = [2] var_b = [3] def _closure(x): return var_a[0] + var_b[0] + x def _magic(y): var_a[0] = y return _closure, _magic localClosureFoo = foo() a = localClosureFoo(1) print a localClosureBar, localClosureBarMAGIC = bar() b = localClosureBar(1) print b localClosureBarMAGIC(0) b = localClosureBar(1) print b