有没有办法修改闭包中其中一个变量的绑定值?看一下这个例子来更好地理解它。
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
答案 0 :(得分:25)
由于nonlocal的魔力,在python 3中很有可能。
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)
答案 1 :(得分:20)
我认为在Python中没有办法做到这一点。定义闭包时,将捕获封闭范围中变量的当前状态,并且不再具有可直接引用的名称(从闭包外部)。如果你再次调用foo()
,新的闭包将与封闭范围有一组不同的变量。
在您的简单示例中,您可能最好使用类:
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
如果你使用这种技术,我将不再使用名称localClosure
,因为它不再是一个闭包。但是,它的工作原理与之相同。
答案 2 :(得分:10)
我找到了Greg的另一个答案答案,稍微不那么冗长,因为它使用了Python 2.1的自定义函数属性(可以方便地从他们自己的函数中访问)。
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
以为我会发布它是为了完整性。无论如何欢呼。
答案 3 :(得分:9)
我们已经完成了以下工作。我认为这比其他解决方案简单。
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
答案 4 :(得分:4)
我通过使用单项列表而不是普通变量来解决类似的限制。它很难看,但它的工作原理是因为修改列表项不会被解释器视为绑定操作。
例如:
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])
答案 5 :(得分:0)
为什么不制作函数foo的var_a和var_b参数?
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
答案 6 :(得分:0)
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
答案 7 :(得分:0)
与询问的内容略有不同,但是您可以:
def f():
a = 1
b = 2
def g(x, a=a, b=b):
return a + b + x
return g
h = f()
print(h(0))
print(h(0,2,3))
print(h(0))
,并将闭包设置为默认值,并在需要时将其覆盖。
答案 8 :(得分:0)
也许还有更进一步的方法(即使我的提议似乎为时已晚:-)
def foo():
def _closure(x):
return _closure.var_a + _closure.var_b + x
_closure.var_a = 2
_closure.var_b = 3
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
# ...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
print(b)
从我的角度来看,提议的课堂解决方案更容易阅读。但是,如果您尝试修改装饰器中的自由变量,此解决方案可能会派上用场:与基于类的解决方案相比,使用 functools.wraps 更容易保留装饰函数的元数据。