在Python中修改闭包的绑定变量

时间:2008-12-24 23:38:31

标签: python functional-programming closures

有没有办法修改闭包中其中一个变量的绑定值?看一下这个例子来更好地理解它。

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

9 个答案:

答案 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 更容易保留装饰函数的元数据。