Python OOP入門:変数の種類とスコープを理解しよう

Pythonのオブジェクト指向プログラミング(OOP)では、変数の種類やその「スコープ(有効範囲)」を正しく理解することが非常に重要です。

本記事では、初級者向けに以下の内容を丁寧に解説します。

これらをマスターすれば、クラス設計やデバッグが格段にラクになります。それでは順を追って見ていきましょう。


スコープとは

スコープは、プログラム中で名前(変数名・関数名など)が「どこから参照できるか」を規定する範囲のことです。

PythonにおけるLEGBルール

Pythonは変数を探索するとき、以下の順序で名前空間を検索します。

  1. Local:現在の関数・メソッドの内部
  2. Enclosing:入れ子関数の外側の関数
  3. Global:そのモジュールのトップレベル
  4. Built-in:Python インタプリタが用意した名前
x = "グローバル"

def outer():
    x = "外側"
    def inner():
        x = "内側"
        print(x)  # 内側
    inner()
    print(x)      # 外側

outer()
print(x)          # グローバル

ライフタイムとは

ライフタイムは、変数が「メモリ上に実際に存在し、値を保持している期間」のことです。


マングリングとは

マングリングは、クラス定義内で名前を __var(ダブルアンダースコア)としたとき、内部的に _<ClassName>__var という別名に書き換える仕組みです。

目的

サブクラスや外部からの意図しないアクセス・名前衝突を防ぎ、疑似的な「アクセス制御」を実現します。

挙動
class A:
    def __init__(self):
        self.__secret = 42   # 実際の属性名は _A__secret
a = A()
print(hasattr(a, "_A__secret"))  # ⇒ True
print(a._A__secret)              # ⇒ 42
# ただし a.__secret では AttributeError

変数の種類

クラス変数(Class Variable)

定義場所・スコープ

  • クラス定義直下で宣言。
  • そのクラスおよびサブクラスのすべてのインスタンスで 共有 される。

ライフタイム

  • プログラム実行中、クラスがメモリ上に存在している限り。

振る舞い

  • インスタンスから self.変数名 でアクセスするとき、同名のインスタンス変数がなければクラス変数が参照される。
  • どこかで ClassName.変数名 = 値 と書くと、すべてのインスタンスに反映される。

使いどころ

全インスタンスで共有したい定数や設定値などで使用します。

class Dog:
    species = "Canis familiaris"    # クラス変数

    def __init__(self, name):
        self.name = name            # インスタンス変数

d1 = Dog("Pochi")
d2 = Dog("Kuro")
print(d1.species, d2.species)      # ⇒ Canis familiaris Canis familiaris

Dog.species = "Canis lupus"
print(d1.species, d2.species)      # ⇒ Canis lupus Canis lupus

インスタンス変数(Instance Variable)

定義場所・スコープ

  • 通常、__init__ メソッド内やクラスメソッド内で self.変数名 = 値 として宣言。
  • その 特定のインスタンス にのみ属する。

ライフタイム

インスタンスが生きている間保持し、有効範囲は当該オブジェクト内です。

振る舞い

同じクラスの別インスタンスとは独立でインスタンスごとに異なる値を持てます。

使いどころ

ユーザーごとの情報や一意に変化させたいデータを保持します。

class Counter:
    def __init__(self):
        self.count = 0            # インスタンス変数

c1 = Counter()
c2 = Counter()
c1.count += 5
print(c1.count, c2.count)        # ⇒ 5 0

グローバル変数(Global Variable)

定義場所・スコープ

  • モジュール(.py ファイル)のトップレベルで宣言。
  • そのモジュール内のすべての関数やクラスから参照可能。
  • 別モジュールからは import モジュール名; モジュール名.変数名 でアクセス。

ライフタイム

プログラム実行中ずっと保持します。

振る舞い

  • 関数内で「読み取り」するだけならそのまま参照可。
  • 関数内で「書き換え」する場合は先頭に global 変数名 が必要。

使いどころ

アプリケーション全体で共有すべき設定値や定数。ただし多用は可読性・保守性を損なうので注意が必要です。

# config.py
VERSION = "1.0.0"   # グローバル変数

# main.py
import config
def show_version():
    print(config.VERSION)

def update_version():
    global VERSION    # ※これは main.py 内の VERSION を指すので要注意
    VERSION = "2.0.0"

show_version()      # ⇒ 1.0.0

プライベート変数(擬似プライベート)

Python には他言語のような完全 private は存在しませんが、以下の慣習・仕組みがあります。

接頭辞動作用途
_var単一アンダースコア:非公開の「慣習」外部から使わないでね、という合意
__varダブルアンダースコア:名前マングリング__ClassName__var に変換され、直接アクセスしづらくなる
名前マングリングの例
class Secret:
    def __init__(self):
        self._hint = "not so secret"     # 慣習的非公開
        self.__data = "top secret"       # マングリングされる

s = Secret()
print(s._hint)          # ⇒ not so secret   (できるけどやらないほうがよい)
print(s.__data)         # ⇒ AttributeError
# 実際には s._Secret__data とすればアクセス可能
print(s._Secret__data)  # ⇒ top secret
  • 慣習として:単一アンダースコアは「内部実装だから外部呼び出しを避けてね」というサイン。
  • 技術的保護として:ダブルアンダースコアで名前を変えてしまい、サブクラスや外部からの衝突や誤アクセスを防ぐ。

ローカル変数(local variable)

定義場所・スコープ

関数・メソッド内で x = … のように定義すると、その関数のブロック内だけで有効です。

ライフタイム

関数/メソッド呼び出し時にスタック上に確保され、呼び出しが終わると破棄される。

振る舞い

同じ関数内でのみ参照・更新可能。デフォルトでは外側のスコープには影響しない。

使いどころ

関数内の計算結果やループカウンタ、一時的なデータの保持。

主な用途

  • 外部に副作用を持たずに処理を完結させる
  • コードの可読性・保守性を高めるための局所的な変数管理
def f(x):
    y = x * 2   # y はローカル変数
    return y

非ローカル変数/キャプチャ変数(Enclosing or Free Variable)

定義場所・スコープ

ネストした関数(入れ子関数)の外側のローカル変数を、内側の関数が参照/修飾するときに現れる。

ライフタイム

変数宣言元の関数(外側の関数)が実行中(呼び出し中)の間。外側関数が戻ると、その変数も破棄される。

振る舞い

内側の関数から nonlocal 宣言で参照・更新できる。グローバル変数とは異なり、モジュール外側ではなく「ひとつ内側」のスコープを指す。

使いどころ

クロージャでの状態保持、内部関数から外部関数の変数を更新したいとき。

主な用途

  • クロージャを使った状態管理(カウンタ、メモ化など)
  • 一度だけ初期化した値を関数間で共有し、再帰やコールバックで更新する場合
def outer():
    counter = 0           # 非ローカル変数(enclosing)
    def inner():
        nonlocal counter  # outer の counter を操作
        counter += 1
        return counter
    return inner
f = outer()
print(f(), f())          # ⇒ 1 2

ビルトイン変数(builtin names)

定義場所・スコープ

Python が最初から用意している名前空間(__builtins__)の中

ライフタイム

Python インタプリタの実行開始から終了まで。__builtins__ モジュールとして常に参照可能。

振る舞い

名前解決ルールで、ローカル → グローバル → ビルトインの順に探索。スコープに同名がなければビルトインを使う。

使いどころ

組み込み関数(len, print 等)、組み込み例外(ValueError 等)、型(int, list 等)を利用したいとき。

主な用途

  • 最低限のユーティリティ機能をいつでも利用できる
  • カスタム関数を書く際の土台(オーバーライドも可能だが注意)
print(len([1,2,3]))  # len はビルトイン関数

注意:同名でローカルやグローバルに変数を定義するとビルトインが隠されてしまう。


環境変数(environment variables)

定義場所・スコープ

OS/シェルのプロセス環境。Python の標準変数ではないが、os.environ 経由でアクセス可能。

ライフタイム

OS/シェルのプロセス環境として、プロセス起動時に読み込まれ、プロセス終了まで有効。子プロセスへ継承される。

振る舞い

os.environ で参照・変更できるが、os.environ[‘KEY’]=’value’ しても親プロセスの環境は変わらない。

使いどころ

  • API キーや設定値など、ソースコードにハードコーディングしたくない機密情報
  • 環境依存設定の注入・CI/CD のビルド時設定

主な用途

  • 設定の切り替え(開発/本番など)
  • パスやトークンなどの管理・外部サービス接続情報の安全な受け渡し
import os

print(os.environ.get('DATABASE_URL', 'sqlite:///:memory:'))
print(os.environ["HOME"])

まとめ表

変数の種類定義場所スコープ・共有範囲主な用途
クラス変数クラス定義直下クラス/サブクラスのすべてのインスタンスで共有定数や共通設定
インスタンス変数__init__ 内など各インスタンスごとインスタンス固有データ
グローバル変数モジュールトップレベルモジュール内全体/他モジュールからも import 可能アプリ全体で使う設定・定数
プライベート変数クラス内(_/__外部からのアクセスを「避ける」「困難にする」内部実装の隠蔽、名前衝突の回避
ローカル変数関数・メソッド内関数内部(ローカル)一時データ、計算結果保持
非ローカル変数ネストした関数(入れ子関数)の外側のローカル変数を、内側の関数が参照/修飾するとき外側関数のスコープクロージャの状態管理
ビルトイン変数最初から用意されている全スコープ(最後の探索先)標準機能の利用
環境変数OS/シェルのプロセス環境プロセス全体アプリ設定・機密情報の注入・切り替え

演習問題

問題1

次のコードの出力を予想してください。

x = 5

class A:
    x = 10
    def print_x(self):
        x = 20
        print(x)
        print(self.x)
        print(globals()['x'])
a = A()
a.print_x()
問題2

クラス Person に population というクラス変数を追加し、インスタンスを生成するたびに population がインクリメントされるように実装してください。

問題3

以下の関数 f を書き換え、内側の関数 g から外側の変数 msg を変更できるようにしてください。

def f():
    msg = "Hello"
    def g():
        msg = "Hi"
    g()
    print(msg)

f()  # Hello → Hi と出力させたい
問題4

モジュール変数 CONFIG を定義し、その中に DEBUG = True が格納されているとして、関数内でこれを False に変更する方法を説明してください。


解答例

解答1
20   ← ローカル変数 x
10   ← インスタンス変数 self.x(クラス変数も参照可能)
5    ← グローバル変数 x
解答2
class Person:
    population = 0  # クラス変数

    def __init__(self, name):
        self.name = name
        Person.population += 1

p1 = Person("太郎")
p2 = Person("花子")
print(Person.population)  # 2
解答3
def f():
    msg = "Hello"
    def g():
        nonlocal msg
        msg = "Hi"
    g()
    print(msg)

f()  # Hi
解答4

モジュールのトップレベルで定義された変数は、関数内で global を使って書き換えられます。

# config.py
CONFIG = {"DEBUG": True}

# main.py
import config

def disable_debug():
    config.CONFIG["DEBUG"] = False

disable_debug()
print(config.CONFIG["DEBUG"])  # False

これらを理解し、適切に使い分けることで、可読性・保守性の高いクラス設計が可能になります。

以上で初級編「PythonのOOPにおける変数の種類とスコープ」の解説と演習問題を終わります。

この記事を読んで、クラス設計やデバッグ時に変数のスコープを正しく意識できるようになりましょう!