この記事では、Pythonのメタクラスについて解説します。
この記事を読んで学べること
- メタクラスとは
- メタクラスの使い方
- メタクラスでできること
メタクラスとは?
メタクラスとは「クラスを生成するクラスのこと」です。 Pythonではメタクラスであるtype()を使うことで動的にクラスを生成することができます。
type()は引数が1つの場合に型を返し、引数が3つの場合にメタクラスとして動作します。
class type(object) class type(name, bases, dict, **kwds)
実際にtype()を使ってクラスを定義してみます。以下のコードのクラスは同等です。
# 通常のクラス定義 class Demo: def __init__(self): print('demo') Demo() # demo # type() を使ったクラス定義 def __init__(self): print('demo') Demo = type('Demo', (), {'__init__': __init__}) Demo() # demo
type()を使って動的にクラスを定義することができました。このようにメタクラスとはクラスを生成するクラスのことを言います。
メタクラスの変更
メタクラスは以下のように指定できる。
class Demo(metaclass=メタクラス): ...
デフォルトは以下のようになっている。
class Demo(metaclass=type): ...
メタクラスは子クラスにも適用されます。
class Demo(metaclass=メタクラス): ... # このクラスもメタクラスで生成される class Child(Demo): ...
メタクラスの定義
メタクラスは自分で定義することができます。簡単な例としてコンストラクタだけを定義したメタクラスを適当なクラスに適用してみます。
# メタクラス class MyMeta: def __init__(cls, name, bases, attrs): print('MyMeta init') # 空のクラスにメタクラスを指定 class MyClass(metaclass=MyMeta): pass
実行結果
MyMeta init
__init__()でクラス変数を追加することもできる。
class MyMeta(type): def __init__(cls, name, bases, attrs): cls.attr = '属性' class MyClass(metaclass=MyMeta): pass print(MyClass.attr) # 属性
__call__()を使うことでインスタンスに属性を追加することもできます。
class MyMeta(type): def __call__(cls, *args, **kwds): obj = super().__call__(*args, **kwds) obj.instance = 'インスタンス変数' return obj class MyClass(metaclass=MyMeta): pass m = MyClass() print(m.instance) # インスタンス変数
メタクラスでできること
メタクラスを使うことでクラスの定義が完了する前にクラスの属性やメソッドを自動的に変更・追加することができます。基本的には__new__()や__init__()と同じような実装が考えられるが、これらのメソッドではできないことを補うのが主な使い方になります。
また、__new__()などで実装できるけど色々なクラスに適用させたい場合もメタクラスで定義すると楽できるかも。以下のコードは、メタクラスを使ってシングルトンを実装した例です。
class SingletonMetaClass(type): def __init__(cls, name, bases, attrs): cls._instance = None def __call__(cls, *args, **kwds): if cls._instance is None: cls._instance = super().__call__(*args, **kwds) return cls._instance class Singleton(metaclass=SingletonMetaClass): pass a = Singleton() b = Singleton() print(a is b) # True
このように、メタクラスを適用するだけでシングルトンを実装することができる。
まとめ
この記事では、Pyhtonのメタクラスについて解説しました。
メタクラスは、フレームワークやライブラリの開発、特定のプログラミングパターンの実装など高度な場面でよく使われます。あまり使う機能ではないので無理に覚えておく必要もありません。
それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ


