225 votes

Je ne comprends pas pourquoi UnboundLocalError se produit (fermeture)

Qu'est-ce que je fais de mal ici ?

counter = 0

def increment():
  counter += 1

increment()

Le code ci-dessus lance un UnboundLocalError .

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.

228voto

Sven Marnach Points 133943

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.

97voto

Andrew Clark Points 77748

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'

30voto

kindall Points 60645

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.

7voto

Rik Poggi Points 10195

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)

5voto

Chris Taylor Points 25079

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.com

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.

Powered by:

X