Qu'est-ce que je fais mal ici?
counter = 0
def increment():
counter += 1
increment()
Le code ci-dessus jette un UnboundLocalError
.
Python n'a pas de déclaration de variable, il doit donc comprendre la portée des variables elle-même. Cela se fait par une simple règle: s'il existe une affectation à une variable dans une fonction, cette variable est considérée comme locale.[1] Ainsi, la ligne
counter += 1
rend implicitement counter
local à increment()
. Essayer d'exécuter cette ligne essaiera cependant de lire la valeur de la variable locale counter
avant son affectation, ce qui donnera un UnboundLocalError
.[2]
Si counter
est une variable globale, le mot clé global
vous aidera. Si increment()
est une fonction locale et counter
une variable locale, vous pouvez utiliser nonlocal
dans Python 3.x.
Vous devez utiliser instruction globale pour modifier le compteur de variable globale au lieu d'une variable locale:
counter = 0
def increment():
global counter
counter += 1
increment()
Si la portée englobante dans laquelle counter
est définie n'est pas la portée globale, vous pouvez utiliser le Python 3.x avec le instruction nonlocal . Dans la même situation sur Python 2.x, vous ne pouvez pas réaffecter le nom non local counter
, vous devez donc rendre counter
mutable et le modifier:
counter = [0]
def increment():
counter[0] += 1
increment()
print counter[0] # prints '1'
Pour répondre à la question dans votre ligne de sujet, * oui, il existe des fermetures en Python, sauf qu'elles s'appliquent uniquement à l'intérieur d'une fonction et qu'elles sont également en lecture seule (en Python 2.x); vous ne pouvez pas relier le nom à un autre objet (cependant, si l'objet est mutable, vous pouvez en modifier le contenu). Dans Python 3.x, vous pouvez utiliser le mot clé nonlocal
pour modifier une variable de fermeture.
def incrementer():
counter = 0
def increment():
nonlocal counter
counter += 1
return counter
return increment
increment = incrementer()
increment() # 1
increment() # 2
* Le titre de la question originale était interrogé sur les fermetures en Python.
La raison pour laquelle votre code jette un UnboundLocalError
est déjà bien expliquée dans d'autres réponses.
Mais il me semble que vous essayez de construire quelque chose qui fonctionne comme itertools.count()
.
Alors pourquoi ne pas l'essayer et voir si cela convient à votre cas:
>>> from itertools import count
>>> counter = count(0)
>>> counter
count(0)
>>> next(counter)
0
>>> counter
count(1)
>>> next(counter)
1
>>> counter
count(2)
Python a une portée lexicale par défaut, ce qui signifie que, bien qu'une portée incluse puisse accéder aux valeurs de sa portée englobante, elle ne peut pas les modifier (à moins qu'elles soient déclarées globales avec le mot clé global
).
Une fermeture lie des valeurs dans l’environnement englobant à des noms dans l’environnement local . L'environnement local peut ensuite utiliser la valeur liée et même réaffecter ce nom à un autre nom, mais il ne peut pas modifier la liaison dans l'environnement englobant.
Dans votre cas, vous essayez de traiter counter
comme une variable locale plutôt que comme une valeur liée. Notez que ce code, qui lie la valeur de x
attribuée dans l'environnement englobant, fonctionne correctement:
>>> x = 1
>>> def f():
>>> return x
>>> f()
1
Pour modifier une variable globale dans une fonction, vous devez utiliser le mot clé global.
Lorsque vous essayez de faire cela sans la ligne
global counter
à l'intérieur de la définition d'incrément, une variable locale nommée counter est créée afin de vous empêcher d'enregistrer la variable counter dont dépend tout le programme.
Notez que vous ne devez utiliser global que lorsque vous modifiez la variable; vous pouvez lire le compteur de manière incrémentée sans avoir besoin de l'instruction globale.
essaye ça
counter = 0
def increment():
global counter
counter += 1
increment()
Python n'est pas purement lexical.
Voir ceci: tilisation de variables globales dans une fonction autre que celle qui les a créées
et ceci: http://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/