Pythonの関数のスコープを制御するnonlocalとglobalの使い方をやさしく解説!初心者でもわかる変数の範囲と変更
生徒
「先生、Pythonで変数を関数の中で使うときに、変数がどこまで使えるかってどうやって決まるんですか?」
先生
「それは『スコープ』という考え方で決まります。スコープとは、変数がどこで見えて使えるかの範囲のことです。」
生徒
「関数の中と外で変数が違うって聞いたことがあります。どうやって外の変数を関数の中で変えられるんですか?」
先生
「そのときに役立つのが global と nonlocal というキーワードです。今回はこの二つの使い方をわかりやすく説明しますね。」
1. Pythonの変数スコープとは?
変数のスコープとは、その変数が使える範囲のことです。例えば、関数の中で作った変数は、その関数の外からは基本的に見えません。これは、プログラムが変数の名前が重ならないように管理するためのルールです。
スコープの例えで言うと、お部屋の中だけで使える机の引き出しみたいなものです。お部屋の外からはその引き出しの中身は見えないし使えません。
2. global(グローバル)キーワードとは?
globalは、「関数の中からプログラム全体で使う変数(グローバル変数)を変更したい」ときに使います。
たとえば、外側にある変数を関数の中で変更したい場合、globalを使わないと関数の中で新しく別の変数を作ったことになってしまいます。
count = 0 # グローバル変数
def increment():
global count # グローバル変数を使う宣言
count += 1 # グローバル変数を変更
increment()
print(count) # 1 と表示される
このようにglobalを使うことで、関数の外にあるcount変数を直接変えることができます。
3. nonlocal(ノンローカル)キーワードとは?
nonlocalは、ネストした関数(関数の中の関数)で、外側の関数にある変数を変更したいときに使うキーワードです。
普通は、内側の関数で外側の関数の変数を書き換えられません。そんなときにnonlocalを使います。
def outer():
count = 0 # 外側の関数の変数
def inner():
nonlocal count # 外側の変数を変更したい宣言
count += 1
inner()
print(count) # 1 と表示される
outer()
ここでnonlocalを使うことで、内側のinner関数から外側のouter関数のcount変数を変更できます。
4. globalとnonlocalの違いは?
両方とも関数の中で外の変数を変えたいときに使いますが、変数の場所が違います。
globalは、プログラム全体のグローバル変数(関数の外で定義された変数)を操作するときに使います。nonlocalは、ネストした関数のすぐ外側の関数にある変数を操作するときに使います。
例えるなら、globalは家の外にある大きな箱、nonlocalは部屋の中の隣の引き出しのようなイメージです。
5. globalとnonlocalを使わないとどうなる?
次のコードを見てください。globalやnonlocalを使わない場合、変数を書き換えたつもりでも、新しいローカル変数ができてしまいます。
count = 0
def increment():
count = count + 1 # エラーになる!
increment()
このコードはエラーになります。なぜなら、count = count + 1の時、右側のcountはグローバル変数ですが、左側は新しく作ったローカル変数になるため、混乱が生じます。
この場合はglobal countを関数内で宣言しておけばエラーになりません。
6. globalとnonlocalの使いどころは?
globalはプログラム全体で使う設定やカウンターなど、どこでも使いたい変数を関数内で変更したいときに使います。
nonlocalは関数の中に関数があって、外側関数の変数を内側関数で変更したい時に使います。
ただし、使いすぎるとプログラムが複雑になるので、必要なときだけ使うのがポイントです。
7. 実践例:nonlocalを使ったカウントアップ関数
nonlocalを使って、外側関数のカウンターを内側関数で増やす例です。
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
count_up = counter()
print(count_up()) # 1
print(count_up()) # 2
print(count_up()) # 3
この例では、countの値が関数を呼ぶたびに増えていきます。nonlocalがないと、countを書き換えられません。
8. 注意点:globalやnonlocalの使い過ぎは避けよう
これらのキーワードは便利ですが、プログラムの中であちこちで使うと、変数の管理が難しくなりバグの原因になります。
できるだけ関数に必要なデータは引数として渡す方法を優先し、どうしても必要な場合にglobalやnonlocalを使うのが良いです。
まとめ
今回の記事では、Pythonのプログラミングにおいて避けては通れない「変数のスコープ(有効範囲)」と、その制御に欠かせないglobal(グローバル)およびnonlocal(ノンローカル)キーワードについて詳しく解説してきました。変数のスコープを正しく理解することは、予期せぬバグを防ぎ、読みやすくメンテナンス性の高いコードを書くための第一歩です。
Pythonのスコープルール「LEGB」の復習
Pythonでは、変数がどこで定義されたかによって、その変数が参照できる優先順位が決まっています。これを「LEGBルール」と呼びます。
- L (Local): 関数やクラスの内部で定義された変数。その中だけで有効です。
- E (Enclosing): ネスト(入れ子)構造になった関数の、外側の関数で定義された変数。
- G (Global): モジュールの最上位レベルで定義された変数。プログラム全体から参照可能です。
- B (Built-in): Pythonが最初から用意している組み込み関数や変数(lenやprintなど)。
通常、関数の中から外側の変数(GlobalやEnclosing)を参照することは可能ですが、直接値を書き換える(再代入する)ことはできません。もし書き換えようとすると、Pythonは「それは新しいローカル変数だ」と解釈してしまい、意図した動作にならないのです。そこで登場するのが、今回学んだ2つのキーワードです。
globalキーワードによる広域変数の操作
globalキーワードは、関数の中から「モジュールのトップレベルにある変数」を更新したいときに使用します。大規模なシステム開発では、グローバル変数の多用は推奨されませんが、設定情報の保持やシステム全体のフラグ管理など、どうしても必要な場面が存在します。
# システム全体のログイン状態を管理するグローバル変数
is_logged_in = False
def login_user():
global is_logged_in
# ログイン処理が成功したと仮定してフラグを更新
is_logged_in = True
print("ログインに成功しました。")
def check_status():
if is_logged_in:
print("現在ログイン中です。")
else:
print("ログインしていません。")
login_user()
check_status()
上記コードの実行結果は以下のようになります。
ログインに成功しました。
現在ログイン中です。
もし、login_user関数内でglobal is_logged_inを宣言しなかった場合、関数の中に同名の新しいローカル変数が作られるだけで、外側のis_logged_inはFalseのままになってしまいます。
nonlocalキーワードによるクロージャの実現
nonlocalキーワードは、Python 3から導入された比較的新しい機能です。これは「関数の中にある関数」から、一つ外側の関数の変数を操作するために使います。これを利用することで、状態を保持し続ける関数(クロージャ)を簡単に作成できます。
def create_accumulator(initial_value):
# 外側の関数の変数
total = initial_value
def add(value):
nonlocal total
total += value
return total
return add
# 10から始まる加算器を作成
my_acc = create_accumulator(10)
print(my_acc(5)) # 15
print(my_acc(20)) # 35
実行結果は以下の通りです。
15
35
この仕組みを使うと、グローバル変数を汚染することなく、特定の関数専用の「メモリ(記憶)」を持たせることができます。クラスを作るまでもないようなシンプルな状態保持には、非常に有効なテクニックです。
使い分けのポイントと注意点
改めて整理すると、globalは「一番外側」、nonlocalは「関数の外側(でもグローバルではない)」を指すと覚えましょう。
| キーワード | 対象となるスコープ | 主な利用シーン |
|---|---|---|
| global | プログラム全体(モジュール単位) | 設定値の変更、共有フラグの更新 |
| nonlocal | ネストした関数の外側のローカルスコープ | クロージャの実装、状態を保持する関数 |
ただし、これらのキーワードを過剰に使用すると、コードの流れが追いづらくなり、予期しない場所で変数が書き換わってしまうリスクが生じます。関数の設計思想としては、「引数でデータを受け取り、戻り値で結果を返す」という純粋な形を目指すのが基本です。どうしても副作用(外側への影響)が必要な場合にのみ、これらを慎重に使用するようにしましょう。
生徒
「先生、ありがとうございました!globalとnonlocalの違いがハッキリ分かりました。結局、どこにある変数を変えたいかで使い分ければいいんですね。」
先生
「その通りです。プログラムの一番外側にある変数をいじりたいならglobal、関数が重なっている時に一つ外側の関数の変数をいじりたいならnonlocal、と覚えれば完璧ですよ。」
生徒
「さっきのクロージャの例(nonlocalを使った加算器)は面白いですね。クラスを使わなくても、関数が『合計値』をずっと覚えておけるなんて驚きました。」
先生
「そうですね。Pythonの柔軟なところです。ただ、生徒くん、注意点もありましたよね?」
生徒
「はい!使いすぎると、どこで変数が変わったか分からなくなってバグの元になるんですよね。基本は関数の引数と戻り値をしっかり使って、ここぞという時にだけ使うようにします。」
先生
「素晴らしい理解です!特にglobalをたくさん使うと、他のプログラムと組み合わせた時に名前が衝突しやすくなるので、気をつけましょう。これでスコープの制御はバッチリですね。次は、この知識を活かしてデコレータなど、より高度なPythonの機能に挑戦してみましょうか!」
生徒
「はい、頑張ります!デコレータでもnonlocalが活躍しそうな予感がします!」