Pythonにおけるオブジェクト指向プログラミングの応用:リフレクションと特殊メソッド

1. 型の検査と継承関係の確認

Pythonでは、オブジェクトの型やクラスの継承関係を動的に確認するための組み込み関数が用意されています。

class Device:
    pass

class Laptop(Device):
    pass

macbook = Laptop()

# インスタンスの型を確認
print(isinstance(macbook, Laptop))  # True
print(isinstance(macbook, Device))  # True (継承関係も考慮される)

# クラスの継承関係を確認
print(issubclass(Laptop, Device))   # True
print(issubclass(Device, Laptop))   # False

2. リフレクション(内省)

リフレクションとは、プログラムの実行中にオブジェクトの属性やメソッドを操作する能力のことです。Pythonでは主に4つの関数を使用します。

class WebServer:
    def __init__(self, host, port):
        self.host = host
        self.port = port

    def start(self):
        print(f"Server starting on {self.host}:{self.port}")

server = WebServer("localhost", 8080)

# 属性の存在確認
if hasattr(server, "start"):
    # 属性の取得
    method = getattr(server, "start")
    method()

# 属性の動的な設定
setattr(server, "protocol", "HTTPS")
print(server.protocol)

# 属性の削除
delattr(server, "port")

これらの手法は、プラグインシステムの構築や動的なルーティング処理などで非常に有効です。

3. 特殊メソッドによるオブジェクトのカスタマイズ

Pythonのクラスは、ダブルアンダースコア(`__`)で囲まれた特殊メソッドを定義することで、演算子や組み込み関数の挙動をカスタマイズできます。

文字列表現(__str__ と __repr__)

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __str__(self):
        # print() や str() で呼ばれる
        return f"商品名: {self.name}"

    def __repr__(self):
        # 開発用・デバッグ用の表現
        return f"Product(name='{self.name}', price={self.price})"

item = Product("Laptop", 150000)
print(item)          # 商品名: Laptop
print(repr(item))    # Product(name='Laptop', price=150000)

コンテナとしての振る舞い(Itemシリーズ)

辞書のように角括弧 `[]` を使ってアクセスできるようにします。

class CustomConfig:
    def __init__(self):
        self._data = {}

    def __setitem__(self, key, value):
        self._data[key] = value

    def __getitem__(self, key):
        return self._data.get(key, "Not Found")

    def __delitem__(self, key):
        if key in self._data:
            del self._data[key]

config = CustomConfig()
config["timeout"] = 30
print(config["timeout"]) # 30

4. インスタンス生成の制御とシングルトン

`__new__` は実際のインスタンスを生成するメソッドで、`__init__` より先に実行されます。これを利用して、クラスから一つのインスタンスしか生成されない「シングルトン」を実現できます。

class DatabaseConnection:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, db_name):
        # 既に初期化されている場合はスキップする等のロジック
        self.db_name = db_name

db1 = DatabaseConnection("Postgres")
db2 = DatabaseConnection("MySQL")

print(db1 is db2)  # True (同一インスタンス)

5. 高度なデコレータ:@property, @classmethod, @staticmethod

クラスの設計をより洗練させるために使用されます。

class Employee:
    bonus_rate = 0.1

    def __init__(self, salary):
        self._salary = salary

    @property
    def total_income(self):
        """メソッドを属性のように呼び出す"""
        return self._salary * (1 + self.bonus_rate)

    @classmethod
    def set_bonus(cls, new_rate):
        """クラス全体の状態を変更する"""
        cls.bonus_rate = new_rate

    @staticmethod
    def is_work_day(day):
        """インスタンスやクラスの状態に依存しないユーティリティ"""
        return day not in ["Saturday", "Sunday"]

emp = Employee(500000)
print(emp.total_income) # 550000.0
Employee.set_bonus(0.2)
print(emp.total_income) # 600000.0

6. 多重継承とMRO(メソッド解決順序)

Python 3では「新式クラス」が採用されており、多重継承時のメソッド探索順序は「C3線形化」アルゴリズムに従います。`mro()` メソッドでその順序を確認できます。

class A:
    def greet(self): print("A")

class B(A):
    def greet(self): print("B")

class C(A):
    def greet(self): print("C")

class D(B, C):
    pass

d = D()
d.greet() # B
print(D.mro()) # [D, B, C, A, object]

タグ: Python OOP Reflection DesignPatterns MetaProgramming

5月16日 15:00 投稿