この記事では、Pythonのローカル変数とスコープについて、プログラミングを始めたばかりの方がつまずきやすいポイントを、分かりやすく解説していきます!
関数の中で作った変数が外で使えなくて「なんで!?」ってなった経験、ありませんか?実はそれ、Pythonの「スコープ」というルールが関係しているんです。
この記事を読めば、変数がどこで使えてどこで使えないのか、その仕組みがバッチリ理解できるようになりますよ!もう変数で悩むのはおしまいです!
この記事で学べること
- ローカル変数ってそもそも何?
- スコープっていう考え方
- 関数の中で変数がどう扱われるかのルール
- グローバル変数とのちがい
- よく見るエラーの原因と対策
- 変数を上手に使うコツ
Pythonプログラミングにおけるローカル変数とスコープの重要性
プログラミングをしていると、
「あれ、この変数さっき作ったはずなのに使えないぞ?」
「同じ名前の変数を使ったら、なんか値がおかしくなった!」
なんて場面に遭遇しませんか?
実は、これらの「あるある」な悩みは、今回お話しするローカル変数とスコープを理解すると、スッキリ解決することが多いんです。
スコープっていうのは、変数が「有効な範囲」のこと。これをちゃんと分かっていると、
- 変数名の衝突を防いで、予期せぬエラーを減らせる
- コードが整理されて、後から見ても分かりやすくなる
- プログラムの部品(関数とか)を作りやすくなる
といった、いいことがたくさんあります。ちょっとだけルールを覚えるだけで、あなたのPythonコードがぐっとレベルアップしますよ!
Pythonのローカル変数とは?定義と基本的な役割
じゃあまず、「ローカル変数」から見ていきましょう。
ローカル変数は、ものすごく簡単に言うと、「特定の場所(主に関数の中)だけで使える変数」のことです。関数の中でちょっとした計算結果を一時的に入れておいたり、関数の中だけで使う情報をしまっておくための、いわば「関数専用の引き出し」みたいなものですね。
例えば、こんな感じ。
# あいさつを作る関数 def greet(name): # この message がローカル変数 message = "こんにちは、" + name + "さん!" print(message) # 関数を呼び出す greet("山田")
このコードの `message` っていう変数がローカル変数です。`greet` 関数の中だけで意味を持っていて、その関数が終わると役割も終わります。
他の場所で同じ `message` という名前の変数を使っても、この関数の中の `message` とは全く別物として扱われるので、ごちゃごちゃになりにくいのが良いところです。
Pythonにおけるスコープとは?変数の有効範囲を理解しよう
次に「スコープ」です。スコープは、「その変数がプログラムのどの範囲から見えるか、使えるか」という、変数の「縄張り」みたいなものです。
なんでこんな縄張りが必要かっていうと、もし全ての変数がどこからでも使えたら、大きなプログラムになったときに大変なことになるからです。あっちこっちで同じ名前の変数が使われて、意図しないところで値が書き換わっちゃったりして、もう大混乱!
スコープがあるおかげで、変数が使える範囲が限定されて、他の場所への影響を心配せずにプログラミングできるわけです。
大きく分けて、関数の中だけで使える「ローカルスコープ」と、プログラムのどこからでも基本的に使える「グローバルスコープ」があります。まずはこの二つを意識するところから始めましょう。
イメージ図
プログラム全体 (グローバルスコープ) +-----------------------------------+ | | | global_var = "どこでも使えるよ" | | | | def my_function(): | | +---------------------------+ | | | 関数の中 (ローカルスコープ) | | | | | | | | local_var = "中だけね" | | | | print(local_var) | | | | print(global_var) | | ← グローバル変数は見える | +---------------------------+ | | | | my_function() | | # print(local_var) ← ここからは見えない (エラーになる) | +-----------------------------------+
Pythonのローカルスコープをコードで確認
言葉だけだとピンとこないかもしれないので、実際のコードでローカルスコープの動きを見てみましょう。
def my_func(): # これは my_func のローカル変数 animal = "ネコ" print("関数の中:", animal) # 関数を呼び出す my_func() # 関数の外からローカル変数にアクセスしようとすると...? print("関数の外:", animal) # ← ここでエラーが発生!
これを実行すると、どうなると思いますか?
実行結果(予想)
関数の中: ネコ Traceback (most recent call last): File "<stdin>", line 9, in <module> NameError: name 'animal' is not defined
ほら!関数の外から `animal` を使おうとしたら、`NameError: name 'animal' is not defined` って怒られちゃいました。「そんな名前の変数は定義されてないよ!」って言われちゃったわけです。
これがローカルスコープの力!`animal` は `my_func` 関数の中だけの存在なので、関数が終わったら、もう外の世界には存在しないことになっているんです。
関数内で定義された変数のスコープ範囲
もうちょっと具体的に見てみましょう。関数の中で定義された変数は、その関数が実行されている間だけ存在し、その関数の中からしか基本的にアクセスできません。
def scope_test(): # 関数スコープテスト用のローカル変数 local_message = "これは関数の中からのメッセージ!" print("scope_test関数の中:", local_message) # 関数呼び出し scope_test() # もう一度、外からアクセス試行 # print(local_message) # ← やっぱりエラーになる!コメントアウトを外すとエラー # 別の関数を定義 def another_func(): # こちらもローカル変数(上のlocal_messageとは別物) local_message = "こっちは別の関数だよ!" print("another_func関数の中:", local_message) # scope_testのlocal_messageにはアクセスできない # print(scope_test.local_message) # ←こんな書き方もできません! another_func()
実行結果
scope_test関数の中: これは関数の中からのメッセージ! another_func関数の中: こっちは別の関数だよ!
このように、`scope_test` 関数の `local_message` と `another_func` 関数の `local_message` は、たまたま名前が同じなだけで、全くの別物です。それぞれの関数という「部屋」の中でしか通用しない変数なんですね。
ローカル変数のライフサイクル生成と消滅
ローカル変数は、関数が呼び出されるたびに新しく「生まれ」て、関数が処理を終えると「消滅」します。なんだか儚い存在ですね。
関数が呼び出されると、その関数専用のメモリ領域が用意され、ローカル変数はそこに作られます。そして、関数の処理がreturn文や処理の終わりまで到達すると、そのメモリ領域は解放され、ローカル変数も一緒に消えてしまう、という流れです。
def counter_func(): # 毎回初期化されるローカル変数 count = 0 count += 1 print("カウント:", count) print("1回目の呼び出し") counter_func() print("2回目の呼び出し") counter_func() print("3回目の呼び出し") counter_func()
実行結果
1回目の呼び出し カウント: 1 2回目の呼び出し カウント: 1 3回目の呼び出し カウント: 1
`counter_func` を何度呼び出しても、`count` は毎回 0 から始まって 1 で終わりますよね。
これは、呼び出されるたびに新しい `count` 変数が生まれて、関数が終わると消滅しているからです。前の関数の `count` の値は引き継がれません。
Pythonのグローバル変数とローカル変数の違い
ここで、「ローカル変数」と対になる存在、「グローバル変数」についても触れておきましょう。
グローバル変数は、関数の外、つまりプログラムのトップレベルで定義される変数です。ローカル変数とは違って、基本的にプログラムのどこからでもアクセス(参照)できます。
まさに「グローバル」な存在感!
違いをまとめるとこんな感じ。
- 定義場所
- ローカル変数: 関数の中
- グローバル変数: 関数の外(トップレベル)
- 有効範囲 (スコープ)
- ローカル変数: 定義された関数の中だけ
- グローバル変数: プログラム全体
- アクセス
- ローカル変数: 定義された関数の中からのみ
- グローバル変数: 基本的にどこからでも参照可能
コードで見てみると一目瞭然です。
# これはグローバル変数 global_var = "僕はグローバル!" def my_function(): # これはローカル変数 local_var = "僕はローカル!" print("関数の中 (ローカル):", local_var) print("関数の中 (グローバル参照):", global_var) # 関数内からグローバル変数を参照 # 関数呼び出し my_function() print("関数の外 (グローバル):", global_var) # print("関数の外 (ローカル):", local_var) # ← ローカル変数は外からアクセスできない!(エラー)
実行結果
関数の中 (ローカル): 僕はローカル! 関数の中 (グローバル参照): 僕はグローバル! 関数の外 (グローバル): 僕はグローバル!
グローバル変数の `global_var` は関数の内外どちらからでもアクセスできていますが、ローカル変数の `local_var` はやはり関数の中からしかアクセスできませんね。
PythonのスコープルールLEGBとは?変数の探索順序
Pythonは、ある変数名が使われたときに、それがどの変数を指しているのかを特定のルールに従って探します。そのルールが、通称「LEGBルール」です。こ
れは、変数を探すスコープの順番を示しています。
- L (Local) ローカルスコープ
まず、関数の中(今いる場所)を探します。変数が見つかればそれを使います。 - E (Enclosing function locals) 外側の関数のローカルスコープ
関数が入れ子になっている場合(関数の中に関数がある場合)、自分を囲んでいる外側の関数に変数がないか探します。 - G (Global) グローバルスコープ
関数の外、プログラムのトップレベルで定義された変数がないか探します。 - B (Built-in) 組み込みスコープ
Pythonにもともと用意されている関数名や変数名(`print`とか`len`とか)を探します。
Pythonくんは、変数が見つかるまで L → E → G → B の順番で健気に探し回るんですね。
# G (Global) x = "グローバルX" def outer_func(): # E (Enclosing) x = "外側関数X" def inner_func(): # L (Local) をコメントアウトしてみる # x = "内側関数X" print("inner_funcの中:", x) # LEGBルールに従って探す inner_func() print("outer_funcの中:", x) outer_func() print("グローバル:", x)
`inner_func` の中の `x = "内側関数X"` をコメントアウトすると、`inner_func` はまずローカル(L)を探しますが、ないので次に外側の関数(E)の `x` を見つけます。だから "外側関数X" と表示されます。
もし `outer_func` の `x = "外側関数X"` もコメントアウトしたら、さらに外側のグローバル(G)を探しに行き、"グローバルX" が見つかる、というわけです。この順番、しっかり覚えておきましょう!
Pythonローカル変数とスコープでよくあるエラーと解決策
スコープの仕組みが分かっていないと、思わぬエラーに遭遇することがあります。ここでは、特によく見るエラー2つとその対処法を見ていきましょう!
NameError変数が定義されていません
これは一番シンプルで、かつ遭遇しやすいエラーかもしれません。
`NameError: name '変数名' is not defined` は、「そんな名前の変数は(今のスコープでは)知らないよ!」という意味です。
原因としては、
- 単純な変数名のタイプミス
- ローカル変数を定義された関数の外から使おうとした
- 変数を定義する前に使おうとした
などが考えられます。
def some_func(): message = "Hello" # print(mesage) # ← タイプミス! 'mesage' なんて変数は無い # some_func() # print(message) # ← 関数の外からローカル変数を使おうとした!
解決策
- まず、変数名が正しく書かれているか、よーく確認しましょう!
- その変数が使えるスコープ(範囲)で使っているか確認しましょう。関数の中で定義したなら、その中でしか使えません。
- 変数を使うコードよりも前の行で、ちゃんと定義(値の代入など)がされているか確認しましょう。
エラーメッセージをよく読んで、どの変数名でエラーが出ているかを確認するのが第一歩です。
UnboundLocalErrorローカル変数参照前のエラー
これはちょっとトリッキーなエラーです。
`UnboundLocalError: local variable '変数名' referenced before assignment` は、「ローカル変数に値が代入される前に、その変数を使おうとしちゃったよ!」という意味です。
これが起こりやすいのは、関数の中でグローバル変数と同じ名前の変数に値を「代入」しようとした場合です。
count = 10 # グローバル変数 def show_count(): # print(count) # ← ここで参照しようとすると UnboundLocalError になる! count = 5 # ← 関数内で代入しようとしている print("関数の中:", count) # show_count() # ← この呼び出しでエラー発生!
なぜエラーになるかというと、Pythonは関数の中で変数への「代入文」 (`count = 5`) を見つけると、「あ、この `count` はこの関数のローカル変数なんだな!」と判断します。
たとえグローバルに同じ名前の変数があっても、代入があるとその関数内ではローカル変数扱いになるんです。
しかし、そのローカル変数 `count` に実際に値 (`5`) が代入されるのは `count = 5` の行です。それより前の行 (`print(count)`) で `count` を使おうとすると、「ローカル変数 `count` はまだ値が決まってない(代入されてない)から使えないよ!」と怒られちゃうわけです。
解決策
- 関数の中でグローバル変数を参照したいだけなら、関数内でその変数への代入を行わないようにします。
- もし関数の中でグローバル変数に値を代入(変更)したい場合は、後述する `global` キーワードを使います。(ただし、注意が必要!)
- そもそも、関数内ではグローバル変数と同じ名前を使わないようにする、というのも有効な回避策です。
このエラーが出たら、「同じ名前のグローバル変数があって、関数内で代入しようとしてるかも?」と疑ってみてください。
Pythonでローカル変数を使うときの注意点とベストプラクティス
ローカル変数とスコープを理解したら、次はそれをどう活かして、より良いコードを書いていくか、というお話です。いくつかコツと注意点があります。
グローバル変数の安易な使用を避ける理由
グローバル変数はどこからでもアクセスできて便利に見えますが、使いすぎには注意が必要です。
なぜなら、
- どこで値が変わるか分かりにくい
プログラムのあちこちから変更できてしまうので、予期しない場所で値が書き換えられてしまい、バグの原因特定が難しくなります。「この値、いつの間に変わったの!?」となりがちです。 - コードの再利用がしにくい
関数がグローバル変数に依存していると、その関数だけを別のプログラムに持っていって使う、ということが難しくなります。そのグローバル変数も一緒に持っていかないといけなくなります。 - 名前の衝突が起きやすい
特に大きなプログラムでは、他の誰かが同じ名前のグローバル変数を使ってしまうかもしれません。
基本的には、関数が必要な情報は「引数」として受け取り、結果は「戻り値」として返すように設計するのがおすすめです。
こうすることで、関数は自己完結し、分かりやすく、再利用しやすい部品になります。
関数内でグローバル変数を変更するglobal文注意点含む
「でも、どうしても関数の中からグローバル変数の値を変更したいんだ!」という場面も、まあ、なくはないかもしれません。
そういう時のために、Pythonには `global` というキーワードが用意されています。
関数の中で `global 変数名` と宣言すると、「この変数名は、ローカル変数じゃなくて、外にあるグローバル変数のことですよ!」とPythonに伝えることができます。
counter = 0 # グローバル変数 def increment(): global counter # この counter はグローバル変数の counter を指すよ!と宣言 counter += 1 print("カウンター:", counter) increment() increment() print("最終結果:", counter)
実行結果
カウンター: 1 カウンター: 2 最終結果: 2
`global` を使ったことで、関数の中で `counter` の値を変更でき、その結果が関数の外にも反映されていますね。
ただし!前述の通り、グローバル変数の変更はコードを複雑にしがちです。`global` 文の使用は、本当に必要な場合に限定し、慎重に使うようにしましょう。他に設計方法がないか、一度立ち止まって考えてみるのが吉です。
ネストした関数での変数アクセスnonlocal文
もう一つ、ちょっと応用的な話として `nonlocal` キーワードがあります。
これは、関数が入れ子(ネスト)になっている場合に、内側の関数から、自分を直接囲んでいる外側の関数のローカル変数(グローバル変数ではない)を変更したいときに使います。
def outer(): message = "外側のメッセージ" # outer関数のローカル変数 def inner(): nonlocal message # この message は外側の関数の message のことだよ!と宣言 message = "内側から変更!" print("innerの中:", message) inner() print("outerの中:", message) # innerで変更された値が表示される outer()
実行結果
innerの中: 内側から変更! outerの中: 内側から変更!
`inner` 関数の中で `nonlocal message` と宣言したことで、`outer` 関数のローカル変数 `message` を変更できていますね。
`global` が一番外側のグローバル変数を対象にするのに対し、`nonlocal` は自分を囲む一番近い外側の関数の変数を対象にします。使い分けがちょっと難しいかもしれませんが、こういう機能もあるんだな、と頭の片隅に置いておくと良いでしょう。
これもやはり、使いすぎには注意です!
【まとめ】Pythonのローカル変数とスコープをマスターしよう
今回は、Pythonのローカル変数とスコープについて、基本的なところから、よくあるエラー、ちょっとした応用まで見てきました。
大事なポイントをまとめると、
- ローカル変数は関数の中だけで使える変数。
- スコープは変数が使える範囲(縄張り)のこと。
- Pythonは変数を LEGBルール (Local → Enclosing → Global → Built-in) の順で探す。
- 関数外からローカル変数を使うと NameError になる。
- グローバル変数と同じ名前の変数に関数内で代入しようとすると UnboundLocalError になりやすい。
- グローバル変数の使いすぎはコードを分かりにくくするので注意!
- どうしてもグローバル変数を関数内で変更したいときは global 文を使う(慎重に!)。
- ネストした関数の外側のローカル変数を変更したいときは nonlocal 文を使う(さらに慎重に!)。
スコープの考え方を理解するだけで、変数の扱い方がぐっと上手になり、エラーに悩む時間も減るはずです。ぜひ、ご自身のコードで関数を作って、変数がどこから見えてどこから見えないのか、色々試してみてください。
エラーが出ても大丈夫!この記事を思い出して、原因を探ってみましょう。トライ&エラーで、Pythonの変数管理をマスターしていきましょう!
【関連記事】 「Pythonとは?」に答える最初の一歩
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。