1

I'm trying to accomplish something like the following:

def counter():
    _n = 0
    def _increase():
        _n += 1
        return _n
    return _increase

The above example should behave like this:

>>> c = counter()
>>> c()
1
>>> c()
2
>>> c()
3

However, when trying to reproduce this, I get the following error:

>>> c = counter()
>>> c()

UnboundLocalError: local variable '_n' referenced before assignment

It looks like it's trying to find the variable in the local scope, so I changed my code to the following:

def counter():
    _n = 0
    def _increase():
        global _n
        _n += 1
        return _n
    return _increase

It appears that it's able to locate it just fine now, but apparently it's uninitialized, even though I'm executing _n = 0 before even declaring the function.

>>> c = counter()
>>> c()

NameError: name '_n' is not defined

Clearly I'm doing something wrong and I'm not aware of a specific Python behavior in this case.

What am I missing here?

Matias Cicero
  • 25,439
  • 13
  • 82
  • 154

1 Answers1

3

You are looking for the nonlocal keyword. It allows you access variables defined in a surrounding scope, but not the global scope.

def counter():
    _n = 0
    def _increase():
        nonlocal _n
        _n += 1
        return _n
    return _increase

Now it should work as you intended.

>>> c = counter()
>>> c()
1
>>> c()
2
>>> c()
3
drevicko
  • 14,382
  • 15
  • 75
  • 97
James
  • 32,991
  • 4
  • 47
  • 70
  • Amazing! I was not aware of this keyword at all! Is this something new on Python 3.x? Never seen it on 2.x code. Thanks for the quick answer! – Matias Cicero Feb 18 '19 at 17:56
  • Yes, it was added in v3. – James Feb 18 '19 at 19:55
  • As I understand it, `nonlocal` searches enclosing scopes (nearest first) till it finds the named variable (taking the first it finds). It requires the variable to be defined at some point, but won't use a global variable. (If this understanding is wrong, please add a note here...) – drevicko Jul 03 '20 at 04:45