なぜ?Pythonのローカル変数が関数外で使えない理由とスコープの基本

2025年4月24日木曜日

Python

この記事では、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ルール」です。こ

れは、変数を探すスコープの順番を示しています。

  1. L (Local) ローカルスコープ
    まず、関数の中(今いる場所)を探します。変数が見つかればそれを使います。
  2. E (Enclosing function locals) 外側の関数のローカルスコープ
    関数が入れ子になっている場合(関数の中に関数がある場合)、自分を囲んでいる外側の関数に変数がないか探します。
  3. G (Global) グローバルスコープ
    関数の外、プログラムのトップレベルで定義された変数がないか探します。
  4. 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とは?」に答える最初の一歩

このブログを検索

  • ()

自己紹介

自分の写真
リモートワークでエンジニア兼Webディレクターとして活動しています。プログラミングやAIなど、日々の業務や学びの中で得た知識や気づきをわかりやすく発信し、これからITスキルを身につけたい人にも役立つ情報をお届けします。 note → https://note.com/yurufuri X → https://x.com/mnao111

QooQ