Don't understand why UnboundLocalError occurs (closure)

PythonScopeClosuresGlobal Variables

Python Problem Overview


What am I doing wrong here?

counter = 0

def increment():
  counter += 1

increment()

The above code throws an UnboundLocalError.

Python Solutions


Solution 1 - Python

Python doesn't have variable declarations, so it has to figure out the scope of variables itself. It does so by a simple rule: If there is an assignment to a variable inside a function, that variable is considered local.[1] Thus, the line

counter += 1

implicitly makes counter local to increment(). Trying to execute this line, though, will try to read the value of the local variable counter before it is assigned, resulting in an UnboundLocalError.[2]

If counter is a global variable, the global keyword will help. If increment() is a local function and counter a local variable, you can use nonlocal in Python 3.x.

Solution 2 - Python

You need to use the global statement so that you are modifying the global variable counter, instead of a local variable:

counter = 0

def increment():
  global counter
  counter += 1

increment()

If the enclosing scope that counter is defined in is not the global scope, on Python 3.x you could use the nonlocal statement. In the same situation on Python 2.x you would have no way to reassign to the nonlocal name counter, so you would need to make counter mutable and modify it:

counter = [0]

def increment():
  counter[0] += 1

increment()
print counter[0]  # prints '1'

Solution 3 - Python

To answer the question in your subject line,* yes, there are closures in Python, except they only apply inside a function, and also (in Python 2.x) they are read-only; you can't re-bind the name to a different object (though if the object is mutable, you can modify its contents). In Python 3.x, you can use the nonlocal keyword to modify a closure variable.

def incrementer():
    counter = 0
    def increment():
        nonlocal counter
        counter += 1
        return counter
    return increment

increment = incrementer()

increment()   # 1
increment()   # 2

* The question origially asked about closures in Python.

Solution 4 - Python

The reason of why your code throws an UnboundLocalError is already well explained in other answers.

But it seems to me that you're trying to build something that works like itertools.count().

So why don't you try it out, and see if it suits your case:

>>> from itertools import count
>>> counter = count(0)
>>> counter
count(0)
>>> next(counter)
0
>>> counter
count(1)
>>> next(counter)
1
>>> counter
count(2)

Solution 5 - Python

Python has lexical scoping by default, which means that although an enclosed scope can access values in its enclosing scope, it cannot modify them (unless they're declared global with the global keyword).

A closure binds values in the enclosing environment to names in the local environment. The local environment can then use the bound value, and even reassign that name to something else, but it can't modify the binding in the enclosing environment.

In your case you are trying to treat counter as a local variable rather than a bound value. Note that this code, which binds the value of x assigned in the enclosing environment, works fine:

>>> x = 1

>>> def f():
>>>  return x

>>> f()
1

Solution 6 - Python

To modify a global variable inside a function, you must use the global keyword.

When you try to do this without the line

global counter

inside of the definition of increment, a local variable named counter is created so as to keep you from mucking up the counter variable that the whole program may depend on.

Note that you only need to use global when you are modifying the variable; you could read counter from within increment without the need for the global statement.

Solution 7 - Python

try this

counter = 0

def increment():
  global counter
  counter += 1

increment()

Solution 8 - Python

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRandomblueView Question on Stackoverflow
Solution 1 - PythonSven MarnachView Answer on Stackoverflow
Solution 2 - PythonAndrew ClarkView Answer on Stackoverflow
Solution 3 - PythonkindallView Answer on Stackoverflow
Solution 4 - PythonRik PoggiView Answer on Stackoverflow
Solution 5 - PythonChris TaylorView Answer on Stackoverflow
Solution 6 - PythonchucksmashView Answer on Stackoverflow
Solution 7 - PythonLostsoulView Answer on Stackoverflow
Solution 8 - PythonMarcinView Answer on Stackoverflow