Modify bound variables of a closure in Python

前端 未结 8 1992
醉话见心
醉话见心 2020-12-03 02:14

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
           


        
相关标签:
8条回答
  • 2020-12-03 02:51

    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
    
    0 讨论(0)
  • 2020-12-03 03:00

    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.

    0 讨论(0)
  • 2020-12-03 03:05

    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.

    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 localClosure because it is no longer actually a closure. However, it works the same as one.

    0 讨论(0)
  • 2020-12-03 03:07

    Why not make var_a and var_b arguments of the function 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
    
    0 讨论(0)
  • 2020-12-03 03:08

    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])
    
    0 讨论(0)
  • 2020-12-03 03:09

    It is quite possible in python 3 thanks to the magic of 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)
    
    0 讨论(0)
提交回复
热议问题