¿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/