代入より前に参照したUnboundLocalErrorの原因
関数内で変数に代入すると、Pythonはその変数を自動的にローカル変数とみなします。代入より前に参照すると発生します。
エラーメッセージの読み方
Traceback (most recent call last):
main.py- ファイル名
5- 行番号 — 実際にクラッシュした行
increment- 発生場所 — <module> ならトップレベル、関数名ならその関数の中
UnboundLocalError- 例外クラス — 何が起きたか。ここを検索するのが最短です
cannot access local variable 'count' where it is not associated with a value- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 count = 0 2 3 def increment(): 4 ^ 5 count = count + 1 6 return count 7 8 print(increment())
関数内で変数に代入すると、Pythonはその変数を自動的にローカル変数とみなします。グローバル変数を書き換えたい場合はglobal宣言が必要です。
直し方: (空) を global count にします。
パターン2
1 total = 100 2 3 def add_tax(): 4 ^ 5 total = total * 1.1 6 return total 7 8 print(add_tax())
代入している行が後にあっても、Pythonは関数全体を先読みしてtotalをローカル変数と判断します。読み込む行が先に実行されるため、その時点でまだ値がありません。
直し方: (空) を global total にします。
パターン3
1 flag = False 2 3 def toggle(): 4 ^ 5 flag = not flag 6 return flag 7 8 print(toggle())
真偽値の反転のような単純な処理でも同じです。関数の外の変数を書き換えるにはglobal宣言が必要です。
直し方: (空) を global flag にします。
パターン4
1 items = [] 2 3 def add_item(x): 4 ^ 5 items = items + [x] 6 return items 7 8 print(add_item(5))
リストであっても規則は同じです。関数内で代入している時点でローカル変数扱いになります。
直し方: (空) を global items にします。
パターン5
1 score = 0 2 3 def add_point(): 4 ^ 5 score = score + 1 6 return score 7 8 print(add_point())
得点を1つ増やすだけの単純な処理でも、global宣言を忘れると発生します。
直し方: (空) を global score にします。
よくある誤解
関数の外で同名の変数が定義されていても、関数内で一度でも代入している変数はローカル扱いになります。読み込む前に代入されているかどうかは関係なく、関数全体を見て判断されます。
実務での勘所
この判定は実行時に1行ずつ決まるのではなく、CPythonがその関数のコードを実行する前に、関数の中に代入文がある変数をあらかじめ「ローカル変数」としてリストアップしておくという、コンパイル段階に近い仕組みで行われます。そのため、代入文が実際に実行される前の行でその変数を参照すると、外側のスコープを探しに行くことなく「ローカル変数として登録されているのに値がまだ無い」というUnboundLocalErrorになります。外側の変数を明示的に使いたい場合は、globalまたはnonlocalという宣言でこの自動判定を上書きする必要があります。