閉じる

Pythonのローカル変数とグローバル変数: スコープの基礎

Pythonでの変数はどこから見えるか(スコープ)を理解すると、意図しないバグを大幅に減らせます。

本記事では、初心者がつまずきやすいローカル変数とグローバル変数の挙動を、短いコードと出力を通して丁寧に解説します。

読み書きの規則と注意点を押さえて、安全で読みやすいコードを書けるようになりましょう

Pythonのスコープとは

変数が使える範囲(スコープ)の基本

スコープとは、変数名が有効な範囲のことを指します。

Pythonでは関数を境にスコープが分かれ、関数内で定義した変数は関数の外から見えません。

一方、ファイル直下(モジュールレベル)で定義した変数は、基本的にそのファイル内の関数から読むことができます。

Pythonのスコープ探索は一般にLEGB(Local→Enclosing→Global→Builtins)という規則で行われますが、本記事ではLocalとGlobalを中心に扱います

以下はスコープの概観です。

名称役割備考
ローカル(Local)関数内の変数や引数関数内だけで有効関数を抜けると参照不可
外側(Enclosing)ネストした外側関数の変数内側関数から参照可変更はnonlocalが必要(本記事範囲外)
グローバル(Global)ファイル直下の変数同じファイルの関数から参照可変更はglobalが必要
組み込み(Builtins)lenprintどこからでも参照可組み込み名の上書きに注意

スコープは「どこから見えるか」の話で、オブジェクトの寿命(ライフタイム)とは別概念です。

例えば関数がオブジェクトを返せば、関数の外でもそのオブジェクト自体は生き続けます。

ローカル変数とグローバル変数の違い

ローカル変数は関数の中だけ、グローバル変数はモジュール全体という違いがあります。

以下の例では、関数内で作ったyは外から見えず、NameErrorになります。

Python
# グローバル変数
x = 10

def show():
    # ローカル変数
    y = 5
    print("関数内で見えるx:", x)  # グローバル変数は関数内から「読み取り」は可能
    print("関数内で見えるy:", y)  # ローカル変数は関数内では見える

show()
print("関数外で見えるx:", x)
print("関数外で見えるy:", y)  # ここはエラーになる
実行結果
関数内で見えるx: 10
関数内で見えるy: 5
関数外で見えるx: 10
NameError: name 'y' is not defined

ポイント: yshowのローカル変数なので関数外からアクセスできません。

関数内と関数外の見え方

関数内からグローバルは読めますが、ローカルは関数外に漏れません

安全に確認するため、外からのアクセスでは例外を捕まえます。

Python
b = 42  # グローバル

def function_scope():
    a = "local only"
    print("関数内からaは見える:", a)

def global_scope():
    print("関数内からbは見える:", b)  # グローバルの読み取り

function_scope()
global_scope()
print("関数外からbは見える:", b)
try:
    print("関数外からaは見える:", a)
except NameError as e:
    print("エラー:", e)
実行結果
関数内からaは見える: local only
関数内からbは見える: 42
関数外からbは見える: 42
エラー: name 'a' is not defined

Pythonのローカル変数の基礎

関数で作られ関数外では使えない

ローカル変数は作られた関数の中でしか使えません

関数の外から参照するとNameErrorになります。

Python
def make_local():
    note = "I live in make_local"
    print("関数内:", note)

make_local()

try:
    print("関数外:", note)  # ローカル変数は関数外から見えない
except NameError as e:
    print("エラー:", e)
実行結果
関数内: I live in make_local
エラー: name 'note' is not defined

関数が終わると、noteという名前は解決できなくなります。

ただし関数内で作ったオブジェクト自体が外に返されていれば、オブジェクトは生き続けます

引数はローカル変数として扱われる

関数の引数もローカル変数です。

同名のグローバル変数があっても、引数名が優先して使われます

コード例1(数値)

Python
total = 100  # グローバル

def add_and_show(total, add):
    # ここでの total は引数(=ローカル)。グローバルの total とは別物。
    total = total + add
    print("関数内のtotal:", total)
    return total

print("関数呼び出し前のglobal total:", total)
new_total = add_and_show(total, 50)
print("関数呼び出し後のglobal total:", total)
print("戻り値new_total:", new_total)
実行結果
関数呼び出し前のglobal total: 100
関数内のtotal: 150
関数呼び出し後のglobal total: 100
戻り値new_total: 150

コード例2(ミュータブルなオブジェクト)

Python
items = [1, 2, 3]

def append_item(lst):
    # lst はローカル変数だが、同じリストオブジェクトを参照している
    lst.append(4)
    print("関数内のlst:", lst)

append_item(items)
print("関数外のitems:", items)
実行結果
関数内のlst: [1, 2, 3, 4]
関数外のitems: [1, 2, 3, 4]

引数名はローカルですが、ミュータブルなオブジェクトを渡すと中身の変更は呼び出し元にも影響します。

必要に応じてコピー(list(x)x.copy())を渡すと安全です。

同名ならローカル変数が優先

関数内で代入がある名前は、その関数のローカルとみなされます

同名のグローバルは見えなくなります。

コード例1(上書きではなく「隠す」)

Python
x = 1  # グローバル

def local_priority():
    x = 99  # 同名のローカル変数がグローバルを隠す
    print("関数内のx:", x)

local_priority()
print("関数外のx:", x)  # グローバルは変わっていない
実行結果
関数内のx: 99
関数外のx: 1

コード例2(よくあるエラー: UnboundLocalError)

Python
x = 1

def read_then_assign():
    try:
        print("代入前にxを参照:", x)  # ここでエラー
        x = 2  # この代入があるため、x はローカル扱いになる
    except UnboundLocalError as e:
        print("UnboundLocalError:", e)

read_then_assign()
実行結果
UnboundLocalError: local variable 'x' referenced before assignment

関数内で代入が1回でもあると、その名前は関数全体でローカル扱いです。

グローバルを読みたいだけなら、関数内で同名の代入を避けるか、次章のglobalを使う必要があります。

Pythonのグローバル変数の基礎

関数の外で定義し関数から読める

グローバル変数は、同じファイル内の関数から宣言なしで読み取れます

Python
rate = 1.1  # グローバル

def apply_rate(price):
    # global 宣言なしに読み取りは可能
    return int(price * rate)

print(apply_rate(100))
実行結果
110

関数内で変更するにはglobalが必要

関数内でグローバル変数を更新するにはglobal宣言が必須です。

無いとUnboundLocalErrorになります。

コード例1(globalなしで失敗)

Python
count = 0

def increment_wrong():
    # 下の代入があるため count はローカル扱い → 右辺の count が未定義になる
    count = count + 1

try:
    increment_wrong()
except UnboundLocalError as e:
    print("エラー:", e)
実行結果
エラー: local variable 'count' referenced before assignment

コード例2(globalありで成功)

Python
count = 0

def increment():
    global count  # グローバル変数を更新する宣言
    count += 1
    print("関数内のcount:", count)

increment()
increment()
print("関数外のcount:", count)
実行結果
関数内のcount: 1
関数内のcount: 2
関数外のcount: 2

グローバル変数のデメリット

グローバルは手軽ですが、コードの見通しとテスト容易性を下げることが多いです。

主なデメリットは以下です。

  • 隠れた依存関係が生まれ、関数の入出力だけでは挙動が読めない
  • 並行処理や複数箇所からの更新で予期せぬ状態になる
  • モジュール間で同名の衝突や上書きが起こり得る

コード例(共有状態による影響)

Python
discount = 0.1  # 共有状態(グローバル)

def set_campaign(on):
    global discount
    discount = 0.3 if on else 0.1  # グローバルを書き換える

def calc(price):
    return int(price * (1 - discount))  # グローバルに依存

print("通常:", calc(1000))
set_campaign(True)
print("キャンペーン中:", calc(1000))
set_campaign(False)
print("終了後:", calc(1000))
実行結果
通常: 900
キャンペーン中: 700
終了後: 900

結果が直前に呼ばれた関数やアプリの状態に依存しており、テストや再利用が難しくなります。

スコープのベストプラクティス

値は引数と戻り値でやり取りする

状態の受け渡しは引数と戻り値で行い、グローバルに依存しない関数にすると、安全でテストしやすくなります。

Python
def add_points(current, delta):
    # 入力と出力が明確な「純粋」な関数に近づける
    new_total = current + delta
    return new_total

total = 0
total = add_points(total, 10)
total = add_points(total, -3)
print("合計:", total)
実行結果
合計: 7

変数の範囲は最小にする

必要な場所でだけ変数を作ると、名前の衝突や思わぬ上書きを防げます。

Pythonはブロックスコープ(ifやfor)を作りませんが、関数で囲うだけでスコープを限定できます。

Python
def process(values):
    result = []
    for v in values:
        doubled = v * 2  # 必要な最小の範囲でのみ使用
        result.append(doubled)
    return result

print(process([1, 2, 3]))
# print(doubled)  # 関数外なので NameError
実行結果
[2, 4, 6]

globalの使用は最小限にする

どうしても必要な設定や定数以外は、globalを避けるのが基本です。

代替として、設定や状態を引数で渡す方法があります。

コード例(設定を引数で渡す)

Python
def apply_with_config(price, config):
    # 状態は引数の config から受け取る
    return int(price * config["rate"])

cfg = {"rate": 1.1}
print(apply_with_config(100, cfg))

cfg["rate"] = 1.2  # 値を変えたい時は呼び出し側で明示的に変更
print(apply_with_config(100, cfg))
実行結果
110
120

どの値が使われるかが関数の引数に現れるため可読性が上がり、テストで差し替えも容易です。

globalは最小限にとどめましょう。

まとめ

Pythonでは「関数内で代入した名前はローカル」という大原則があり、グローバルを関数内で更新するにはglobalが必須です。

ローカル変数は関数外から見えず、引数はローカルとして扱われます。

同名の変数がある場合、ローカルが優先され、思わぬUnboundLocalErrorの原因になります。

設計としては引数と戻り値で値をやり取りし、変数の範囲を最小にし、globalの使用は最小限にすることで、読みやすく安全なコードを書けます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!