一覧へ

関数の名前空間とスコープ

Pythonで変数を検索するルール(LEGB)と名前空間の動作原理を理解します。

中級
|
10
|
検証済み (2026-07)
namespacescopeLEGBルールglobalnonlocal変数探索
進捗0/18 (0%)

関数の名前空間とスコープ

このトピックを修了すると

Pythonが変数名をどのような順序で検索するか(LEGBルール)を説明でき、ローカル変数とグローバル変数が競合する状況を適切に処理できるようになります。


同じ名前、異なる値

python
x = 10
def foo():
x = 20
print(x)
foo() # 20
print(x) # 10

関数の内部のxと、外部のxは、名前は同じですが、異なる変数です。これを理解するためには、名前空間とスコープを知る必要があります。


名前空間とは

名前空間(namespace)は、名前とオブジェクトのマッピングを格納する空間です。Pythonでは、辞書のように{名前: オブジェクト}の形式で動作します。

python
x = 10
name = "철수"
# モジュール名前空間: {"x": 10, "name": "철수", ...}
python
def greet():
msg = "안녕"
# 関数名前空間: {"msg": "안녕"}

関数を呼び出すと、新しい名前空間が作成され、関数が終了すると消えます。そのため、関数内で作成した変数は、外部で使用することはできません。


スコープとLEGBルール

スコープ(scope)は、コード内で名前が見える範囲です。Pythonは、変数を検索する際に、LEGBの順序で探索します。

text
L — Local        関数内部
E — Enclosing    囲んでいる関数(ネストされた関数)
G — Global       モジュールの最上位
B — Built-in     Python組み込み(print、lenなど)
python
x = "global" # G
def outer():
x = "enclosing" # E
def inner():
x = "local" # L
print(x)
inner()
outer() # "local" — Lで見つかったので、それ以上の探索は行わない

最も内側(L)から探し、見つからない場合は外側に移動します。見つかった時点で探索を停止します。


Local — 関数内部の変数

python
def calculate():
result = 42 # Local変数
temp = result * 2
return temp
calculate()
print(result) # NameError: name 'result' is not defined

関数内で代入(=)した変数は、自動的にLocal変数になります。関数の外部では存在しません。


Global — モジュールの最上位の変数

python
counter = 0 # Global
def increment():
print(counter) # ✅ 読み込みは可能(LEGBでGで見つかる)
def reset():
counter = 0 # ⚠️ 新しいLocal変数が作成される!Globalのcounterとは無関係
reset()
print(counter) # 依然として0(Globalは変更されない)

関数内でGlobal変数を読み込むことは自由にできます。ただし、代入すると、Pythonはそれを新しいLocal変数として認識します。

globalキーワード

Global変数を関数内で変更するには、globalキーワードを使用します。

python
counter = 0
def increment():
global counter
counter += 1
increment()
increment()
print(counter) # 2

global counterは、「この関数内のcounterはLocalではなく、Globalのものである」という宣言です。

ただし、globalを乱用すると、値がどこで変更されているかを追跡するのが難しくなります。可能な限り、関数の引数と戻り値でデータをやり取りすることをお勧めします。


Enclosing — ネストされた関数とnonlocal

python
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
print(inner()) # 1
print(inner()) # 2
outer()

nonlocalは、「この変数はLocalではなく、すぐ外側の関数(Enclosing)のものである」という宣言です。クロージャを作成する際に使用します。


よくある間違い — UnboundLocalError

python
x = 10
def broken():
print(x) # ❌ UnboundLocalError!
x = 20
broken()

エラーが発生します。Pythonは、関数全体を事前に解析し、x = 20がある場合、xをLocal変数として決定します。しかし、print(x)の時点では、まだLocal変数xに値が代入されていないため、エラーが発生します。

解決策:

python
def fixed_1():
x = 20 # まず代入
print(x)
def fixed_2():
global x # Globalであることを明示
print(x)
x = 20

Built-in — Python組み込みの名前

python
print(len([1, 2, 3])) # 3 — lenはBuilt-in
# 誤って組み込みの名前を上書きすると
list = [1, 2, 3] # ⚠️ Built-in listを上書き
new_list = list("abc") # TypeError!

listdictprintlenなどの名前は、Built-in名前空間にあります。これらの名前を変数として使用すると、Built-inが隠され、元の機能が使用できなくなります。

python
# 復旧
del list # Local/Globalのlistを削除すると、Built-inが再び表示される

クロージャ — Enclosingの実践的な活用

python
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15

multiply関数が返された後も、factorを記憶しています。これがクロージャ(closure) — 関数が定義されたスコープの変数を「閉じて保持する」ことです。

python
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3

クラスを作成しなくても、状態を維持する関数を作成できます。


名前空間のデバッグ

python
def debug_scope():
x = 10
y = 20
print(locals()) # {'x': 10, 'y': 20}
debug_scope()
print(globals().keys()) # グローバル名前空間のすべての名前

locals()globals()は、それぞれ現在のスコープの名前空間を辞書として返します。変数がどこに属しているかを確認したい場合に便利です。

python
import builtins
print(dir(builtins)) # Built-in名前空間のすべての名前

重要なまとめ

キーワード意味
(なし)代入 → Local、参照 → LEGBの順序で探索
global「この変数はGlobalのものである」
nonlocal「この変数はEnclosing関数のものである」
text
探索順序:Local → Enclosing → Global → Built-in
代入規則:関数内で=を使用すると、Localとみなされる

モジュールと名前空間

python
import math
from os import path
# importはモジュール名で名前空間を分離
print(math.pi) # math名前空間のpi
print(path.exists) # os.pathのexists
# from import *は名前空間を汚染する
from math import * # ⚠️ すべての名前が現在のGlobalに流れ込む
print(pi) # 動作するが、どこから来たのか不明確

import mathは、mathという名前を1つだけGlobalに追加します。from math import *は、数十の名前をGlobalに流し込み、名前の競合のリスクが高まります。


名前空間の競合によって発生するバグは、デバッグが難しい場合があります。「この変数がどこに属しているか」を常に意識することで、このような間違いを避けることができます。

💬 質問・コメント

0件のコメント

ログインせずに投稿できます。ゲスト投稿は投稿者自身で編集・削除できません。

0/2000

読み込み中...