views:

825

answers:

5

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
A: 

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
titaniumdecoy
That's doesn't quite answer my problem, because I need to modify the values of closures which are already created. I.e. your answer requires me to create a new closure everytime I need to change the bound variables.
Vicent Marti
+10  A: 

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.

Greg Hewgill
Cheers, that did the trick! A tad verbose, but it works.
Vicent Marti
+1: first-class object rather than closure. Closure is a peculiar non-object thing. Objects are clearer and easier to deal with than closures.
S.Lott
There is in fact a way. nonlocal. See my post.
recursive
+4  A: 

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

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.

Vicent Marti
+2  A: 

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)
recursive
...where 'magic' is read 'ugly hackery'
Claudiu
I would certainly use that code. :)
recursive
i would too, it's certainly better than the other way. i just meant that by default it should be nonlocal
Claudiu
Doh! I meant to say I wouldn't. It still looks weird and hacky to me. The whole bit about the optional parameter to change value. Whole thing should be a class. But anyway, I digress.
recursive
A: 

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
Tim James