web-dev-qa-db-ja.com

UnboundLocalErrorが発生する理由がわからない

ここで何が間違っていますか?

counter = 0

def increment():
  counter += 1

increment()

上記のコードはUnboundLocalErrorをスローします。

138
Randomblue

Pythonには変数宣言がないため、変数自体の scope を把握する必要があります。これは、単純なルールによって行われます。関数内の変数への割り当てがある場合、その変数はローカルと見なされます。[1] したがって、行

counter += 1

counterを暗黙的にincrement()のローカルにします。ただし、この行を実行しようとすると、ローカル変数counterの値が割り当てられる前に読み取られ、 UnboundLocalError になります。[2]

counterがグローバル変数である場合、 global キーワードが役立ちます。 increment()がローカル関数で、counterがローカル変数の場合、Python 3.xで nonlocal を使用できます。

145
Sven Marnach

ローカル変数の代わりにグローバル変数カウンターを変更するために、 global statement を使用する必要があります。

counter = 0

def increment():
  global counter
  counter += 1

increment()

counterが定義されている囲みスコープがグローバルスコープでない場合、Python 3.xでは nonlocal statement を使用できます。 Python 2.xの同じ状況では、非ローカル名counterに再割り当てする方法がないため、counterを変更可能にして変更する必要があります。

counter = [0]

def increment():
  counter[0] += 1

increment()
print counter[0]  # prints '1'
76
Andrew Clark

件名の質問に答えるために、はい、Pythonにはクロージャーがあります。ただし、クロージャーは関数内でのみ適用され、(Python 2.xでは)読み取り専用です。名前を別のオブジェクトに再バインドすることはできません(ただし、オブジェクトが変更可能な場合は、その内容を変更できます)。 Python 3.xでは、 nonlocal キーワードを使用してクロージャー変数を変更できます。

def incrementer():
    counter = 0
    def increment():
        nonlocal counter
        counter += 1
        return counter
    return increment

increment = incrementer()

increment()   # 1
increment()   # 2

*元の質問のタイトルは、Pythonのクロージャについて尋ねました。

16
kindall

コードがUnboundLocalErrorをスローする理由は、他の回答ですでに十分に説明されています。

しかし、あなたは itertools.count() のように機能する何かを構築しようとしているように思えます。

それでは、試してみて、それがあなたのケースに合うかどうか確かめてみてください:

>>> 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にはデフォルトで字句スコープがあります。つまり、囲まれたスコープはその囲まれたスコープの値にアクセスできますが、変更できません( global キーワードでグローバルに宣言されていない限り)。

クロージャーは、enclosing環境の値をlocal環境の名前にバインドします。その後、ローカル環境はバインドされた値を使用でき、その名前を他の何かに再割り当てすることもできますが、囲んでいる環境のバインディングを変更することはできません。

あなたの場合、counterをバインドされた値ではなくローカル変数として処理しようとしています。囲んでいる環境で割り当てられたxの値をバインドするこのコードは正常に機能することに注意してください。

>>> x = 1

>>> def f():
>>>  return x

>>> f()
1
4
Chris Taylor

関数内のグローバル変数を変更するには、globalキーワードを使用する必要があります。

行なしでこれを行おうとすると

global counter

incrementの定義の内部で、counterという名前のローカル変数が作成され、プログラム全体が依存する可能性のあるカウンター変数を作成しないようにします。

変数を変更するときのみグローバルを使用する必要があることに注意してください。グローバルステートメントを必要とせずに、増分内からカウンターを読み取ることができます。

3
chucksmash

これを試して

counter = 0

def increment():
  global counter
  counter += 1

increment()
2
Lostsoul

Pythonは純粋に字句的にスコープされていません。

これを参照してください: グローバル変数を作成した関数以外の関数でグローバル変数を使用する

そしてこれ: http://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/

0
Marcin