しかし、この答えは、問題は x への代入にあると言っています。それだけなら、印刷しても問題ないはずですよね?
物事が起こる順序を理解する必要があります。Python コードをコンパイルして実行する前に、パーサーと呼ばれるものが Python コードを読み取り、構文をチェックします。パーサーが行うもう 1 つのことは、変数をローカルとしてマークすることです。パーサーがコード内の割り当てをローカル スコープで検出すると、割り当ての左側の変数がローカルとしてマークされます。その時点では、まだ何もコンパイルされておらず、ましてや実行されていないため、代入は行われません。変数は単にローカル変数としてマークされています。
パーサーが終了すると、コードがコンパイルされて実行されます。実行が print ステートメントに到達すると、次のようになります。
def main():
x = 10 #<---x in enclosing scope
def f():
print x #<-----
x = x + 1 #<-- x marked as local variable inside the function f()
print ステートメントは先に進み、囲んでいるスコープ (LEGB ルックアップ プロセスの 'E') に x を出力する必要があるように見えます。ただし、パーサーは以前に x を f() 内のローカル変数としてマークしていたため、Python はローカル スコープ (LEGB ルックアップ プロセスの 'L') を超えて x をルックアップしません。'print x' の実行時にローカル スコープで x が割り当てられていないため、python はエラーを吐き出します。
割り当てが行われるコードが決して実行されない場合でも、パーサーは割り当ての左側の変数をローカル変数としてマークすることに注意してください。パーサーは、物事がどのように実行されるかを認識していないため、実行できないコードであっても、ファイル全体で構文エラーやローカル変数をやみくもに検索します。その例を次に示します。
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
a b c #...yet the parser finds a syntax error here
return f
f = dostuff()
f()
--output:--
File "1.py", line 8
a b c
^
SyntaxError: invalid syntax
パーサーは、ローカル変数をマークするときに同じことを行います。
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
x = 0 #..yet the parser marks x as a local variable
return f
f = dostuff()
f()
最後のプログラムを実行するとどうなるか見てみましょう:
Traceback (most recent call last):
File "1.py", line 11, in <module>
f()
File "1.py", line 4, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
ステートメント「print x」が実行されると、パーサーが x をローカル変数としてマークしたため、x のルックアップはローカル スコープで停止します。
その「機能」は python に固有のものではなく、他の言語でも発生します。
配列の例については、次のように記述します。
x[0] = x[0] + 1
これは、x という名前の配列を検索し、最初の要素に何かを割り当てるように Python に指示します。ローカル スコープには x という名前の割り当てがないため、パーサーは x をローカル変数としてマークしません。