ここで何が間違っていますか?
counter = 0
def increment():
counter += 1
increment()
上記のコードはUnboundLocalError
をスローします。
Pythonには変数宣言がないため、変数自体の scope を把握する必要があります。これは、単純なルールによって行われます。関数内の変数への割り当てがある場合、その変数はローカルと見なされます。[1] したがって、行
counter += 1
counter
を暗黙的にincrement()
のローカルにします。ただし、この行を実行しようとすると、ローカル変数counter
の値が割り当てられる前に読み取られ、 UnboundLocalError
になります。[2]
counter
がグローバル変数である場合、 global
キーワードが役立ちます。 increment()
がローカル関数で、counter
がローカル変数の場合、Python 3.xで nonlocal
を使用できます。
ローカル変数の代わりにグローバル変数カウンターを変更するために、 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'
件名の質問に答えるために、はい、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のクロージャについて尋ねました。
コードが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)
Pythonにはデフォルトで字句スコープがあります。つまり、囲まれたスコープはその囲まれたスコープの値にアクセスできますが、変更できません( global
キーワードでグローバルに宣言されていない限り)。
クロージャーは、enclosing環境の値をlocal環境の名前にバインドします。その後、ローカル環境はバインドされた値を使用でき、その名前を他の何かに再割り当てすることもできますが、囲んでいる環境のバインディングを変更することはできません。
あなたの場合、counter
をバインドされた値ではなくローカル変数として処理しようとしています。囲んでいる環境で割り当てられたx
の値をバインドするこのコードは正常に機能することに注意してください。
>>> x = 1
>>> def f():
>>> return x
>>> f()
1
関数内のグローバル変数を変更するには、globalキーワードを使用する必要があります。
行なしでこれを行おうとすると
global counter
incrementの定義の内部で、counterという名前のローカル変数が作成され、プログラム全体が依存する可能性のあるカウンター変数を作成しないようにします。
変数を変更するときのみグローバルを使用する必要があることに注意してください。グローバルステートメントを必要とせずに、増分内からカウンターを読み取ることができます。
これを試して
counter = 0
def increment():
global counter
counter += 1
increment()
Pythonは純粋に字句的にスコープされていません。
これを参照してください: グローバル変数を作成した関数以外の関数でグローバル変数を使用する
そしてこれ: http://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/