127

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?

Niels Bom
  • 8,728
  • 11
  • 46
  • 62
adinsa
  • 1,271
  • 2
  • 9
  • 3

10 Answers10

132

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
user2357112
  • 260,549
  • 28
  • 431
  • 505
Chris B.
  • 85,731
  • 25
  • 98
  • 139
  • 4
    why is it possible to modify the value from dictionary? – coelhudo Apr 13 '12 at 02:29
  • 9
    @coelhudo Because you **can** modify nonlocal variables. But you cannot do assignment to nonlocal variables. E.g., this will raise UnboundLocalError: `def inner(): print d; d = {'y': 1}`. Here, `print d` reads outer `d` thus creating nonlocal variable `d` in inner scope. – suzanshakya May 01 '12 at 17:52
  • 21
    Thanks for this answer. I think you could improve the terminology though: instead of "can read, cannot change", maybe "can reference, cannot assign to". You can change the contents of an object in a nonlocal scope, but you can't change which object is referred to. – metamatt Feb 04 '13 at 06:46
  • 3
    Alternatively, you can use arbitrary class and instantiate object instead of the dictionary. I find it much more elegant and readable since it does not flood the code with literals (dictionary keys), plus you can make use of methods. – Alois Mahdal Aug 15 '13 at 13:19
  • 6
    For Python I think it is best to use the verb "bind" or "rebind" and to use the noun "name" rather than "variable". In Python 2.x, you can close over an object but you can't rebind the name in the original scope. In a language like C, declaring a variable reserves some storage (either static storage or temporary on the stack). In Python, the expression `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. – steveha Dec 20 '13 at 07:08
  • @AloisMahdal: That's a good idea -- which I turned into an [answer](http://stackoverflow.com/a/21812269/355230) except it doesn't use a arbitrary class. – martineau Feb 16 '14 at 14:14
  • the third code snippet in PEP-3104 describes using a dict as described by Chris B.: https://www.python.org/dev/peps/pep-3104/#new-syntax-in-the-binding-outer-scope – matias elgart Dec 06 '16 at 22:28
  • Is there no `from __future__` import for this? – dspyz Aug 16 '18 at 17:48
39

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)
Community
  • 1
  • 1
Marc van Leeuwen
  • 3,605
  • 23
  • 38
  • It has not to be uggly. Instead of using inner.y, use outer.y. You can define outer.y = 0 before the def inner. – jgomo3 Dec 27 '13 at 14:33
  • 1
    Ok, I see my comment is wrong. Elias Zamaria implemented the outer.y solution also. But as is commented by Nathaniel[1], you should beware. I think this answer should be promoted as the solution, but note about the outer.y solution and cavets. [1] http://stackoverflow.com/questions/3190706/nonlocal-keyword-in-python-2-x#comment21772934_13794589 – jgomo3 Dec 27 '13 at 14:44
  • This gets ugly if you want more than one inner function sharing nonlocal mutable state. Say a `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`. – BlackJack Sep 14 '16 at 08:03
37

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
Bob Stein
  • 16,271
  • 10
  • 88
  • 101
  • Nice! This is indeed more elegant and readable than the dictionary. – Vincenzo Dec 15 '15 at 19:17
  • I would recommend using slots in general here. It protects you from typos. – DerWeh Sep 01 '18 at 10:43
  • @DerWeh interesting, I've [never heard](https://stackoverflow.com/questions/472000/usage-of-slots) of that. Could you say the same of any class? I do hate getting flummoxed by typos. – Bob Stein Sep 01 '18 at 15:19
  • @BobStein Sorry, I don't really understand what you mean. But by adding `__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. – DerWeh Sep 02 '18 at 17:13
  • @DerWeh I never heard of using __slots__ to catch typos. You're right it would only help in class instances, not class variables. Maybe you would like to create a working example on pyfiddle. It would require at least two additional lines. Can't picture how you would do it without more clutter. – Bob Stein Sep 02 '18 at 21:20
  • What if you need to store a function in `y`? It becomes a method! :) And bam - "TypeError: unbound method". – ogurets Jan 18 '19 at 07:22
14

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
Scott Ritchie
  • 10,293
  • 3
  • 28
  • 64
ig0774
  • 39,669
  • 3
  • 55
  • 57
  • Instead of creating a class and instantiating it, one can simply create a function: `def ns(): pass` followed by `ns.x = 3`. It ain't pretty, but it's slightly less ugly to my eye. – davidchambers Jan 25 '11 at 11:36
  • 2
    Any particular reason for the downvote? I admit mine is not the most elegant solution, but it works... – ig0774 Mar 13 '13 at 12:25
  • The second code-sample does not work simply because assignment to a name (variable) that does not exist in the current scope will result in the creation of a local variable with the assigned value. Right? I would not find other behavior logical. – Niels Bom Jul 01 '13 at 13:48
  • @NielsBom This is exactly what he is saying in text. He just wanted to make a point with this code example. – schlamar Jul 04 '13 at 09:01
  • @davidchambers There are many variations to this, a much better but still unintentional way would be `ns = lambda: None`. – schlamar Jul 04 '13 at 09:13
  • Or `def create_namespace(**values): return type('Namespace', (object,), values)` `ns = create_namespace(x=0)` :) – schlamar Jul 04 '13 at 09:16
  • ig0774: It may be more appropriate to put ns.x = 3 inside the scope of outer to answer this question to implement the closure. I'll edit it and see if people agree. – johannestaas Feb 07 '14 at 22:27
  • @schlamar: Why is it that `ns = lambda: None` is better for the most part? More readable, or is the performance better? Would it be better performance than just using a dict? `ns = {"x": 0}` – johannestaas Feb 07 '14 at 22:39
  • @johannestaas Never said that explicitly... This was a reply to the first comment. I meant that a lambda would be more readable/intentional than using a noop function just to get a namespace object. A dict (or a list) is IMO the best way to deal with nonlocal variables on Python 2. – schlamar Feb 10 '14 at 08:09
  • 2
    What about `class Namespace: x = 3`? – Feuermurmel Feb 18 '14 at 20:28
  • 3
    I don't think this way simulates nonlocal, since it creates a global reference instead of a reference in a closure. – CarmeloS Nov 24 '14 at 02:43
  • 1
    I agree with @CarmeloS, your last block of code in _not_ doing what PEP-3104 suggests as way to accomplish something similar in Python 2. `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. – martineau Mar 28 '15 at 15:14
12

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.

Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
  • 21
    Please beware: when implemented this way, if you do `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. – N. Virgo Mar 14 '13 at 03:57
  • @Nathaniel: Let me see if I understand this correctly. The assignment to `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? – Marc van Leeuwen Apr 16 '13 at 08:13
  • 1
    Correction, I should have said, after "bound in that scope": to an object whose type allows setting attributes (like a function, or any class instance). Also, since this scope is actually further out than the one we want, wouldn't this suggest the following extremely ugly solution: instead of `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`? – Marc van Leeuwen Apr 16 '13 at 08:37
  • @MarcvanLeeuwen Looks like your comment inspired the solution with inner.y by Elias. Good thought by you. – Ankur Agarwal Jul 20 '15 at 05:21
  • Accessing and updating global variables is probably not what most people are trying to do when mutating a closure’s captures. – binki Dec 16 '15 at 07:05
12

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)
martineau
  • 119,623
  • 25
  • 170
  • 301
  • This one seems to be the best in terms of readability and preserving lexical scoping. – Aaron S. Kurland Oct 08 '14 at 03:17
  • Your second solution doesn't work if you call `outer()` more than once, as all the calls share the same counter. It's essentially no different from using a global variable. – interjay Apr 06 '21 at 17:48
  • @interjay: True, because function attributes are effectively global variables (as functions are themselves). To fix it would require a way to reset the value of function attributes — which is feasible. – martineau Apr 06 '21 at 18:18
  • Resetting the value is still not good enough because it would prevent you from using two counters in parallel. I would just delete this part of the answer. – interjay Apr 06 '21 at 20:19
3

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.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • Both versions work in this particular case but are not a replacement for `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. – BlackJack Sep 14 '16 at 07:53
  • @BlackJack `nonlocal` is a python 3 feature. – Marcin Sep 14 '16 at 13:42
  • Yes I know. The question was how to achieve the effect of Python 3's `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](https://gist.github.com/Marrin/e00fa440a313e5eb4ee7b1cc857797a4) 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. – BlackJack Sep 14 '16 at 15:31
  • @BlackJack That's not the question, but thanks for your input. – Marcin Sep 14 '16 at 16:03
  • Yes that _is_ the question. Take a look at the top of the page. adinsa has Python 2.6 and needs access to nonlocal variables in a closure without the `nonlocal` keyword introduced in Python 3. – BlackJack Sep 14 '16 at 21:20
3

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
Ezer Fernandes
  • 131
  • 1
  • 6
0

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)
Amnon Harel
  • 103
  • 1
  • 3
-3

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.

Community
  • 1
  • 1
Val
  • 1
  • 8
  • 40
  • 64
  • It is much better to use a dictionary. You can reference the instance in `inner`, but can't assign to it, but you can modify it's keys and values. This avoids use of global variables. – johannestaas Feb 07 '14 at 22:43