Qu'est-ce que je fais de mal ici ?
counter = 0
def increment():
counter += 1
increment()
Le code ci-dessus lance un UnboundLocalError
.
Qu'est-ce que je fais de mal ici ?
counter = 0
def increment():
counter += 1
increment()
Le code ci-dessus lance un UnboundLocalError
.
Python n'a pas de déclarations de variables, il doit donc trouver le nom de la variable portée de variables lui-même. Pour ce faire, il applique une règle simple : si une variable est assignée à l'intérieur d'une fonction, cette variable est considérée comme locale. [1] Ainsi, la ligne
counter += 1
fait implicitement counter
local à increment()
. L'exécution de cette ligne, cependant, essaiera de lire la valeur de la variable locale counter
avant qu'il ne soit affecté, ce qui entraîne un UnboundLocalError
. [2]
Si counter
est une variable globale, le global
Le mot-clé vous aidera. Si increment()
est une fonction locale et counter
une variable locale, vous pouvez utiliser nonlocal
dans Python 3.x.
Vous devez utiliser le déclaration globale de sorte que vous modifiez le compteur de la variable globale, au lieu d'une variable locale :
counter = 0
def increment():
global counter
counter += 1
increment()
Si la portée englobante qui counter
est définie dans n'est pas la portée globale, sur Python 3.x, vous pouvez utiliser l'attribut déclaration non locale . Dans la même situation, sous Python 2.x, vous n'auriez aucun moyen de réassigner le nom non local. counter
donc vous devez faire counter
mutable et le modifier :
counter = [0]
def increment():
counter[0] += 1
increment()
print counter[0] # prints '1'
Pour répondre à la question posée dans votre sujet*, oui, il existe des closures en Python, sauf qu'elles ne s'appliquent qu'à l'intérieur d'une fonction et qu'elles sont en lecture seule (en Python 2.x) ; vous ne pouvez pas relier le nom à un objet différent (mais si l'objet est mutable, vous pouvez modifier son contenu). En Python 3.x, vous pouvez utiliser la fonction 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
* La question portait à l'origine sur les fermetures en Python.
La raison pour laquelle votre code jette un UnboundLocalError
est déjà bien expliqué 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 vous convient :
>>> 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 fermée puisse accéder aux valeurs de sa portée fermée, elle ne peut pas les modifier (à moins qu'elles ne soient déclarées globales avec l'attribut global
mot-clé).
Une fermeture lie les valeurs dans le enfermant à des noms dans l'environnement local l'environnement. L'environnement local peut alors utiliser la valeur liée, et même réaffecter ce nom à quelque chose d'autre, 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
assigné dans l'environnement environnant, fonctionne bien :
>>> x = 1
>>> def f():
>>> return x
>>> f()
1
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
1 votes
Cette question et celle dont elle est actuellement le double sont en cours de discussion au sein de la Chatroom Python .
5 votes
Beaucoup de réponses ici disent d'utiliser
global
et bien que cela fonctionne, l'utilisation de globaux modifiables est généralement no recommander lorsque d'autres options existent.21 votes
@ZeroPiraeus Une question posée en 2012 ne peut pas être un doublon d'une question posée en 2016 ... plutôt la plus récente est le doublon.
10 votes
@dsh Ce n'est pas vrai. .
0 votes
@juanpa.arrivillaga c'est bien que la question générale est fermeture et de mettre à jour une liaison qui n'est pas locale. UnboundLocalError peut également se produire pour des variables entièrement locales, mais il s'agit d'un problème différent (avec une solution différente).
0 votes
@Masklinn oui, mais un la fermeture n'est pas créée dans ce cas . Vous pouvez vérifier,
print(increment.__closure__)
0 votes
@juanpa.arrivillaga c'est un détail d'implémentation. C'est conceptuellement une fermeture, même si Python différencie les globaux et les non-locaux non globaux. La question et la réponse primaire sont pertinentes pour le problème dans les deux cas.
0 votes
@Masklinn OK, tu m'as convaincu. Je suppose qu'au sens large, une variable libre est toute variable non locale. N'hésitez pas à revenir en arrière