メタクラスとディスクリプタは、メタプログラミングのための高度な Python 機能で、クラスや属性アクセスの振る舞いを制御します。これらはフレームワーク(ORM、Pydantic)の多くの「マジック」を支えていますが、アプリケーションコードで直接書くことはまれです。
メタクラス — 「クラスを作るクラス」
:
(Dog)
(Dog())
クラスがインスタンスを作るのと同じように、メタクラスはクラスを作ります。メタクラスをカスタマイズすることで、クラス生成を傍受して変更できます。
class Meta(type):
def __new__(mcs, name, bases, namespace):
# runs when a class USING this metaclass is DEFINED
namespace["created_by"] = "Meta" # inject an attribute into every such class
return super().__new__(mcs, name, bases, namespace)
class MyClass(metaclass=Meta):
pass
MyClass.created_by # "Meta" — injected by the metaclass at class-creation time
これにより、フレームワークはクラスを自動的に登録したり、定義を検証したり、すべてのサブクラスに振る舞いを追加したりできます。例えば、ORM がクラス属性をデータベースのカラムに変換する仕組みです。
ディスクリプタは __get__/__set__/__delete__ を定義するオブジェクトで、属性が読み書きされるときに何が起こるかを制御します。
class Positive:
def __set_name__(self, owner, name):
self.name = f"_{name}"
def __get__(self, obj, objtype=None):
return getattr(obj, self.name)
def __set__(self, obj, value):
if value < 0:
raise ValueError("must be positive") # validate on assignment
setattr(obj, self.name, value)
class Account:
balance = Positive() # a descriptor as a class attribute
a = Account()
a.balance = 100 # calls Positive.__set__ → validates
a.balance = -5 # ❌ ValueError
ディスクリプタは属性アクセスを傍受し、検証、計算属性、遅延ロードを可能にします。実際、@property、@classmethod、@staticmethod はすべてディスクリプタとして実装されています。
Metaclasses → Django/SQLAlchemy ORM models, ABCs, Pydantic, framework registration
Descriptors → @property, ORM fields, validation libraries, lazy attributes
メタクラスとディスクリプタは、Python の最も強力なライブラリの多くを支えるメタプログラミングの機構です。クラス定義をデータベーススキーマに変換する ORM、Pydantic のような検証フレームワーク、さらには @property(ディスクリプタ)のような組み込み機能までもです。
ほとんどの開発者はこれらを直接書くことはまれですが(より単純なツールで十分なときに手を出すべきではありません)、これらを理解することで、こうしたフレームワークがクラスレベルのカスタマイズや属性アクセスの制御という「マジック」をどう実現しているかが明らかになります。
この知識は、高度なライブラリ設計、フレームワークの挙動のデバッグ、Python のオブジェクトモデルの深い理解に役立ちます。それは言語に対する中級者と専門家レベルの理解を分けるものです。