web-dev-qa-db-fra.com

Je ne comprends pas pourquoi UnboundLocalError se produit

Qu'est-ce que je fais mal ici?

counter = 0

def increment():
  counter += 1

increment()

Le code ci-dessus jette un UnboundLocalError.

144
Randomblue

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.

148
Sven Marnach

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'
78
Andrew Clark

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.

16
kindall

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)
7
Rik Poggi

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
4
Chris Taylor

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.

3
chucksmash

essaye ça

counter = 0

def increment():
  global counter
  counter += 1

increment()
2
Lostsoul
0
Marcin