UnboundLocalError:変数が定義されているのに "cannot access local variable" エラーが発生する原因と解決策
Pythonで関数内で外部の変数を操作しようとした際、変数が定義されているにもかかわらず "UnboundLocalError: cannot access local variable 'a' where it is not associated with a value" というエラーが発生することがあります。このエラーはPythonの変数スコープに起因するもので、正しく理解することで簡単に対処できます。
エラーの原因:変数スコープの問題
Pythonの変数には「スコープ」という概念があります。関数内で変数の代入操作を行うと、Pythonインタプリタはその変数を新たなローカル変数として扱います。元のコードではこの仕組みが原因でエラーが発生していました:
a = 0 # グローバル変数
def test():
a += 1 # ここで代入操作が行われるためローカル変数とみなされる
a += 1
はa = a + 1
と等価- 右辺の
a
を評価するタイミングで、関数内で定義されていないローカル変数a
へのアクセスが発生 - 結果として「変数が値に関連付けられていない」というエラーが発生
解決方法1:globalキーワードの使用
関数内でグローバル変数を変更する場合、明示的にglobal
宣言を行います:
import keyboard
import time
a = 0 # グローバル変数
def test():
global a # グローバル変数を使用する宣言
a += 1
print(f"The number is now {a}")
time.sleep(1)
while not keyboard.is_pressed('i'):
test()
global使用時の注意点
- グローバル変数の多用はコードの保守性を低下させます
- 複数関数から同一変数が変更されるとバグ発生リスクが高まります
- 小規模スクリプト以外では避けるのがベストプラクティスです
解決方法2:引数として値を受け渡す (推奨)
より安全なアプローチは、変数を引数で渡し、戻り値で更新した値を返す方法です:
import keyboard
import time
def test(counter): # 引数で値を受け取る
counter += 1
print(f"The number is now {counter}")
time.sleep(1)
return counter # 更新した値を返す
a = 0
while not keyboard.is_pressed('i'):
a = test(a) # 値を渡し、更新結果を受け取る
この方法の利点
- 状態管理が明示的になりバグを減らせる
- 関数の再利用性が向上する
- マルチスレッド環境でも安全に使用できる
# 非推奨: 大規模開発では問題発生リスク有
global a
a += 1
# 推奨: 安全で明示的な方法
def increment(counter):
return counter + 1
補足:nonlocalキーワードについて
別の回答で言及されたnonlocal
は、クロージャがある場合に使用しますが、今回のケースでは適用できません:
def outer():
a = 0
def inner():
nonlocal a # 外側の関数の変数を使用
a += 1
return inner
nonlocalの誤用注意
- nonlocalはグローバル変数には適用不可
- ネストした関数内で、直近の外側スコープの変数を参照する場合のみ有効
ベストプラクティスまとめ
グローバル変数の変更は最小限に
- どうしても必要な場合のみglobalを使用
- 変更箇所を1関数に集中させドキュメント化
状態の受け渡しには引数と戻り値を活用
クラスで状態管理する選択肢も
pythonclass Counter: def __init__(self): self.value = 0 def increment(self): self.value += 1 print(f"Now: {self.value}")
Pythonのスコープルールを正しく理解し、グローバル変数への依存を減らすことで、このエラーを防ぎつつ、保守性の高いコードを実現できます。特に長時間実行されるスクリプト(例中のキー監視ループ)では状態管理が重要です。安全な引数渡しの方法を選択することで予期しないバグを回避しましょう。