No entiendo por qué ocurre UnboundLocalError

¿Qué estoy haciendo mal aquí?

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

El código anterior lanza un UnboundLocalError .

Python no tiene declaraciones de variables, por lo que tiene que determinar el scope de las variables en sí. Lo hace mediante una regla simple: si hay una asignación a una variable dentro de una función, esa variable se considera local. [1] Así, la línea

 counter += 1 

implícitamente hace que el counter local a increment() . Sin embargo, al intentar ejecutar esta línea, se intentará leer el valor del counter de la variable local antes de que se asigne, dando como resultado un UnboundLocalError . [2]

Si el counter es una variable global, la palabra clave global ayudará. Si increment() es una función local y counter una variable local, puede usar nonlocal en Python 3.x.

Debe usar la statement global para modificar el contador de variables globales, en lugar de una variable local:

 counter = 0 def increment(): global counter counter += 1 increment() 

Si el scope que encierra ese counter no es el scope global, en Python 3.x podría usar la statement no local . En la misma situación en Python 2.x no tendría forma de reasignar al counter nombres no locales, por lo que tendría que hacer que el counter mutable y modificarlo:

 counter = [0] def increment(): counter[0] += 1 increment() print counter[0] # prints '1' 

Para responder a la pregunta en su línea de asunto, * sí, hay cierres en Python, excepto que solo se aplican dentro de una función, y también (en Python 2.x) son de solo lectura; no puede volver a vincular el nombre a un objeto diferente (aunque si el objeto es mutable, puede modificar su contenido). En Python 3.x, puede usar la palabra clave nonlocal para modificar una variable de cierre.

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

* El título de la pregunta original era sobre los cierres en Python.

La razón por la que su código arroja un UnboundLocalError ya está bien explicada en otras respuestas.

Pero me parece que estás tratando de construir algo que funciona como itertools.count() .

Entonces, ¿por qué no lo prueba y ve si se adapta a su caso?

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

Para modificar una variable global dentro de una función, debe usar la palabra clave global.

Cuando intentas hacer esto sin la línea

 global counter 

Dentro de la definición de incremento, se crea una variable local denominada contador para evitar que destruya la variable de contador de la que depende todo el progtwig.

Tenga en cuenta que solo necesita usar global cuando está modificando la variable; puede leer el contador desde dentro del incremento sin la necesidad de la statement global.

Python tiene un scope léxico de forma predeterminada, lo que significa que aunque un scope cerrado puede acceder a los valores en su scope, no puede modificarlos (a menos que se declaren globales con la palabra clave global ).

Un cierre enlaza los valores en el entorno adjunto a los nombres en el entorno local . El entorno local puede utilizar el valor enlazado e incluso reasignar ese nombre a otra cosa, pero no puede modificar el enlace en el entorno que lo contiene.

En su caso, está tratando de tratar el counter como una variable local en lugar de un valor limitado. Tenga en cuenta que este código, que enlaza el valor de x asignado en el entorno adjunto, funciona bien:

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

prueba esto

 counter = 0 def increment(): global counter counter += 1 increment() 

Python no tiene un scope puramente léxico.

Vea esto: Usar variables globales en una función que no sea la que las creó

y esto: http://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/