I'm trying to implement a closure in Python 2.6 and I need to access a nonlocal variable but it seems like this keyword is not available in python 2.x. How should one access nonlocal variables in closures in these versions of python?
10 Answers
Inner functions can read nonlocal variables in 2.x, just not rebind them. This is annoying, but you can work around it. Just create a dictionary, and store your data as elements therein. Inner functions are not prohibited from mutating the objects that nonlocal variables refer to.
To use the example from Wikipedia:
def outer():
d = {'y' : 0}
def inner():
d['y'] += 1
return d['y']
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
8 Comments
def inner(): print d; d = {'y': 1}. Here, print d reads outer d thus creating nonlocal variable d in inner scope.X = 1 simply binds the name X with a particular object (an int with the value 1). X = 1; Y = X binds two names to the same exact object. Anyway, some objects are mutable and you can change their values.The following solution is inspired by the answer by Elias Zamaria, but contrary to that answer does handle multiple calls of the outer function correctly. The "variable" inner.y is local to the current call of outer. Only it isn't a variable, since that is forbidden, but an object attribute (the object being the function inner itself). This is very ugly (note that the attribute can only be created after the inner function is defined) but seems effective.
def outer():
def inner():
inner.y += 1
return inner.y
inner.y = 0
return inner
f = outer()
g = outer()
print(f(), f(), g(), f(), g()) #prints (1, 2, 1, 3, 2)
3 Comments
inc() and a dec() returned from outer that increment and decrement a shared counter. Then you have to decide which function to attach the current counter value to and reference that function from the other one(s). Which looks somewhat strange and asymmetrical. E.g. in dec() a line like inc.value -= 1.Rather than a dictionary, there's less clutter to a nonlocal class. Modifying @ChrisB's example:
def outer():
class context:
y = 0
def inner():
context.y += 1
return context.y
return inner
Then
f = outer()
assert f() == 1
assert f() == 2
assert f() == 3
assert f() == 4
Each outer() call creates a new and distinct class called context (not merely a new instance). So it avoids @Nathaniel's beware about shared context.
g = outer()
assert g() == 1
assert g() == 2
assert f() == 5
6 Comments
__slots__ = () and creating an object instead of using the class, e.g. context.z = 3 would raise an AttributeError. It is possible for all classes, unless they inherit from a class not defining slots.I think the key here is what you mean by "access". There should be no issue with reading a variable outside of the closure scope, e.g.,
x = 3
def outer():
def inner():
print x
inner()
outer()
should work as expected (printing 3). However, overriding the value of x does not work, e.g.,
x = 3
def outer():
def inner():
x = 5
inner()
outer()
print x
will still print 3. From my understanding of PEP-3104 this is what the nonlocal keyword is meant to cover. As mentioned in the PEP, you can use a class to accomplish the same thing (kind of messy):
class Namespace(object): pass
ns = Namespace()
ns.x = 3
def outer():
def inner():
ns.x = 5
inner()
outer()
print ns.x
12 Comments
def ns(): pass followed by ns.x = 3. It ain't pretty, but it's slightly less ugly to my eye.class Namespace: x = 3?ns is a global object which is why you can reference ns.x at the module level in the print statement at the very end.There is another way to implement nonlocal variables in Python 2, in case any of the answers here are undesirable for whatever reason:
def outer():
outer.y = 0
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
It is redundant to use the name of the function in the assignment statement of the variable, but it looks simpler and cleaner to me than putting the variable in a dictionary. The value is remembered from one call to another, just like in Chris B.'s answer.
5 Comments
f = outer() and then later do g = outer(), then f's counter will be reset. This is because they both share a single outer.y variable, rather than each having their own independent one. Although this code looks more aesthetically pleasing than Chris B's answer, his way seems to be the only way to emulate lexical scoping if you want to call outer more than once.outer.y does not involve anything local to the function call (instance) outer(), but assigns to an attribute of the function object that is bound to the name outer in its enclosing scope. And therefore one could equally well have used, in writing outer.y, any other name instead of outer, provided it is known to be bound in that scope. Is this correct?outer.y use the name inner.y (since inner is bound inside the call outer(), which is exactly the scope we want), but putting the initialisation inner.y = 0 after the definition of inner (as the object must exist when its attribute is created), but of course before return inner?Here's something inspired by a suggestion Alois Mahdal made in a comment regarding another answer:
class Nonlocal(object):
""" Helper to implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def outer():
nl = Nonlocal(y=0)
def inner():
nl.y += 1
return nl.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
4 Comments
outer() more than once, as all the calls share the same counter. It's essentially no different from using a global variable.Another way to do it (although it's too verbose):
import ctypes
def outer():
y = 0
def inner():
ctypes.pythonapi.PyCell_Set(id(inner.func_closure[0]), id(y + 1))
return y
return inner
x = outer()
x()
>> 1
x()
>> 2
y = outer()
y()
>> 1
x()
>> 3
1 Comment
There is a wart in python's scoping rules - assignment makes a variable local to its immediately enclosing function scope. For a global variable, you would solve this with the global keyword.
The solution is to introduce an object which is shared between the two scopes, which contains mutable variables, but is itself referenced through a variable which is not assigned.
def outer(v):
def inner(container = [v]):
container[0] += 1
return container[0]
return inner
An alternative is some scopes hackery:
def outer(v):
def inner(varname = 'v', scope = locals()):
scope[varname] += 1
return scope[varname]
return inner
You might be able to figure out some trickery to get the name of the parameter to outer, and then pass it as varname, but without relying on the name outer you would like need to use a Y combinator.
5 Comments
nonlocal. locals() creates a dictionary of outer()s locals at the time inner() is defined but changing that dictionary does not change the v in outer(). This won't work any more when you have more inner functions which want to share a closed over variable. Say a inc() and dec() that increment and decrement a shared counter.nonlocal is a python 3 feature.nonlocal in Python 2 in general. Your ideas don't cover the general case but just the one with one inner function. Have a look at this gist for an example. Both inner functions have their own container. You need a mutable object in the scope of the outer function, as other answers suggested already.nonlocal keyword introduced in Python 3.Extending Martineau elegant solution above to a practical and somewhat less elegant use case I get:
class nonlocals(object):
""" Helper to implement nonlocal names in Python 2.x.
Usage example:
def outer():
nl = nonlocals( n=0, m=1 )
def inner():
nl.n += 1
inner() # will increment nl.n
or...
sums = nonlocals( { k:v for k,v in locals().iteritems() if k.startswith('tot_') } )
"""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __init__(self, a_dict):
self.__dict__.update(a_dict)
Comments
Use a global variable
def outer():
global y # import1
y = 0
def inner():
global y # import2 - requires import1
y += 1
return y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
Personally, I do not like the global variables. But, my proposal is based on https://stackoverflow.com/a/19877437/1083704 answer
def report():
class Rank:
def __init__(self):
report.ranks += 1
rank = Rank()
report.ranks = 0
report()
where user needs to declare a global variable ranks, every time you need to call the report. My improvement eliminates the need to initialize the function variables from the user.
1 Comment
inner, but can't assign to it, but you can modify it's keys and values. This avoids use of global variables.