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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/392349/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 19:59:56  来源:igfitidea点击:

Modify bound variables of a closure in Python

pythonfunctional-programmingclosures

提问by Vicent Marti

Is there any way to modify the bound value of one of the variables inside a closure? Look at the example to understand it better.

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

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

采纳答案by Greg Hewgill

I don't think there is any way to do that in Python. When the closure is defined, the current state of variables in the enclosing scope is captured and no longer has a directly referenceable name (from outside the closure). If you were to call foo()again, the new closure would have a different set of variables from the enclosing scope.

我认为在 Python 中没有任何方法可以做到这一点。当闭包被定义时,封闭作用域中变量的当前状态被捕获并且不再具有直接可引用的名称(来自闭包外部)。如果您foo()再次调用,新的闭包将具有与封闭作用域不同的变量集。

In your simple example, you might be better off using a class:

在您的简单示例中,您最好使用一个类:

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

If you do use this technique I would no longer use the name localClosurebecause it is no longer actually a closure. However, it works the same as one.

如果您确实使用了这种技术,我将不再使用该名称,localClosure因为它实际上不再是一个闭包。但是,它的工作原理与 1 相同。

回答by recursive

It is quite possible in python 3 thanks to the magic of nonlocal.

由于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)

回答by Vicent Marti

I've found an alternate answer answer to Greg's, slightly less verbose because it uses Python 2.1's custom function attributes (which conveniently enough can be accessed from inside their own function).

我找到了 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

Thought I'd post it for completeness. Cheers anyways.

以为我会张贴它的完整性。总之干杯。

回答by Tim James

We've done the following. I think it's simpler than other solutions here.

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

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

回答by Justin Grant

I worked around a similar limitation by using one-item lists instead of a plain variable. It's ugly but it works because modifying a list item doesn't get treated as a binding operation by the interpreter.

我通过使用单项列表而不是普通变量来解决类似的限制。这很丑陋,但它有效,因为修改列表项不会被解释器视为绑定操作。

For example:

例如:

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])

回答by slothy

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

回答by Lars

slightly different from what was asked, but you could do:

与所问的略有不同,但你可以这样做:

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))

and make the closure the default, to be overridden when needed.

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

回答by titaniumdecoy

Why not make var_a and var_b arguments of the function foo?

为什么不让函数 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