Why does this UnboundLocalError occur (closure)?

Question:

What am I doing wrong here?

counter = 0

def increment():
  counter += 1

increment()

The above code throws an UnboundLocalError.

Asked By: Randomblue

||

Answers:

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.

Answered By: chucksmash

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'
Answered By: Andrew Clark

Try this:

counter = 0

def increment():
  global counter
  counter += 1

increment()
Answered By: Lostsoul

Python is not purely lexically scoped.

See Using global variables in a function.

And Notes on Python variable scope.

Answered By: Marcin

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.

Answered By: Sven Marnach

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
Answered By: Chris Taylor

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.

Answered By: kindall

The reason 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 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)
Answered By: Rik Poggi