5 ms·
Isn't there an issue in Python where closures don't work as expected? The following code will blow up: https://gist.github.com/skatenerd/72281cadd2ae3e44d6cf h
by skatenerd 12y ago
Isn't there an issue in Python where closures don't work as expected? The following code will blow up:
https://gist.github.com/skatenerd/72281cadd2ae3e44d6cf https://gist.github.com/skatenerd/72281cadd2ae3e44d6cf
For some reason, stashing the variable inside of a list will fix the variable resolution..?
http://stackoverflow.com/questions/4851463/python-closure-write-to-variable-in-parent-scope http://stackoverflow.com/questions/4851463/python-closure-wr...
- ctz 12y agohttp://melpon.org/wandbox/permlink/XsgfjNU0oTsj0GaN http://melpon.org/wandbox/permlink/XsgfjNU0oTsj0GaN nonlocal was added to fix this problem. lists make a difference because they are a mutable container for other values, not because name resolution changes when you use them.
- recursive 12y agoThis is what "nonlocal" is for. That would make the original code work. And putting the value inside a list works because in that case you are mutating the list not assigning a new list to the variable.
- njohnson41 12y agoThe issue is with scoping, not the closure mechanism. Python will by default shadow variables in an enclosing scope instead of overwriting them during assignment, to prevent accidental introduction of global state. You can use the "global" keyword to force overwriting the outer variable. The list hack also works because it is a mutable data structure, but I wouldn't recommend it.
- skatenerd 12y agoInteresting - I can read from the variable but I can't assign to it.
- overgard 12y agoYou can assign to it, you just have to be explicit about that being what you want to do. (Explicit being better than implicit is part of the python philosophy)
- ptx 12y agoPython decides for each name if it refers to a local or non-local (or global) variable when the function is defined. If you assign to it, that fact is used to decide that the name refers to a local variable. Otherwise, it must refer to a global variable or a variable in some parent scope. This means that in the following code, "x" will refer to a local variable for the whole function body: x = "foo" def f(): print(x) x = 42 f() ...so instead of printing "foo" (the value of the global variable) the print statement will raise an exception: UnboundLocalError: local variable 'x' referenced before assignment If you meant it to refer to a global variable or something in the parent scope, simply put "global x" or "nonlocal x", respectively, at the start of the function. Again, that applies to the whole function body, so the assignment in this example would change the value of the global variable.
- ufo 12y agoWhat you actually want is "nonlocal". "global" will force the assignment to be on a global variable.
- maxerickson 12y agoThe list solves the problem because it is okay to mutate an object from an outer scope, whereas the += tries to assign a new value to the name. There are lots of other awful hacks: def a2(start): def a(v): a.v+=v return a.v a.v=start return a
- rm445 12y agoGood point. I think serious python people (not me, I had to search a bit) would understand the scoping well enough to recognise the gotcha. Turns out the problem is the +=, perhaps it would be a bit clearer if written as: grand_total = grand_total + to_add which introduces a new local variable called grand_total shadowing the outer one. So it's straightforward enough, the pitfall is missing the shadowing. Note that you could use the outer grand_total without problems if you don't shadow it, e.g. using it as an rvalue (is that a pythonic term?) , and also note Python 3 provides a 'nonlocal' keyword to let you pull the outer variable into scope so it won't be shadowed.