52

有什么方法可以修改闭包内变量之一的绑定值?查看示例以更好地理解它。

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
4

9 回答 9

48

由于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)
于 2008-12-25T03:13:56.153 回答
22

我认为在 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,因为它实际上不再是一个闭包。但是,它的工作原理与一个相同。

于 2008-12-24T23:58:23.690 回答
11

我找到了 Greg's 的一个替代答案,稍微不那么冗长,因为它使用 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

以为我会发布它以确保完整性。反正干杯。

于 2008-12-25T00:08:52.250 回答
10

我们做了以下事情。我认为它比这里的其他解决方案更简单。

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
于 2010-08-03T17:44:46.333 回答
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])
于 2011-01-01T23:31:30.040 回答
1

也许还有更进一步的方法(即使我的提议似乎为时已晚 :-)

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

于 2021-01-29T22:19:49.790 回答
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
于 2008-12-24T23:53:01.050 回答
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
于 2012-01-09T17:25:59.590 回答
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))

并使闭包成为默认值,在需要时被覆盖。

于 2020-04-02T12:59:03.073 回答