python の __init__, __new__, __metaclass__ や、それらを使ったメタクラスの作成方法などについて。
「メタプログラミング」という言葉はたぶんこれ以外の機能についても言えるんだけど、参考にしたサイトが大体メタプログラミングという言葉を使っていたので、ここでもそうする。
こちらの記事が非常に参考になりました。
Python の メタプログラミング (__metaclass__, メタクラス) を理解する | yunabe.jp
細かい説明は上の記事に書かれているので、ここではポイントだけメモ。
ポイント
- クラス定義は 
typeのインスタンスである MyClass()という書き方でクラスのインスタンスを生成したとき、内部では__new__→__init__の順に呼ばれる__new__の内部ではtypeのインスタンス化(type(cls, names, attrs)の呼び出し)が行われている- クラスに 
__metaclass__という属性があると、typeのインスタンス化のかわりに実行される 
__metaclass__ の基本的な使い方としては:
typeを継承したクラスAを定義する(メタクラス)- 別のクラスBを定義し、メタクラスAをクラスBの 
__metaclass__属性に指定する - → クラスBがインスタンス化された時、クラスAの 
__new__メソッドが呼ばれる。結果としてクラスBのインスタンス作成時の振る舞いをカスタマイズできる 
以下、ポイントを順に見ていく。
クラス定義は type のインスタンスである
以下 (1) と (2) は同じ挙動となる。
# -*- coding: utf-8 -*- class Foo(object): pass # (1) 通常のクラス定義 class Bar(Foo): attr1 = 'bar' def method1(self, *args): print 'method1' def func1(self, *args): print 'method1' # (2) type を使ったクラス定義 # type(クラス名, 親クラス, 属性・メソッドの辞書) Baz = type('Baz', (Foo,), {'attr1': 'baz', 'method1': func1}) bar = Bar() bar.method1() print type(Bar) # True print isinstance(Bar, type) # True baz = Baz() baz.method1() print type(Baz) # True print isinstance(Baz, type) # True
ので、クラスを定義すると内部的には type('クラス名', ...) が実行されてクラスが生成されていることになる。
MyClass() という書き方でクラスのインスタンスを生成したとき、内部では __new__ → __init__ の順に呼ばれる
__new__ の内部では type のインスタンス化(type(cls, names, attrs) の呼び出し)が行われている
python のクラスの初期化は通常 __init__ で行い、このメソッドを他の言語で言うところのコンストラクタのように利用することが多い。
が、インスタンス生成時には __init__ の前に __new__ が呼ばれている。
詳細な順番は http://www.yunabe.jp/docs/python_metaclass.html から引用させていただく。
class ClassNameがClassName()によってインスタンス生成された場合- まず
 ClassName.__new__が第1引数にClassName, 残りの引数にClassNameに与えた残りの引数が与えられて呼び出される。- 普通は
 __new__は定義されていないので親クラスをたどっていって最終的にobject.__new__が呼び出される。object.__new__は第1引数で与えられた クラスのインスタンスを生成して返す。object.__new__が返すインスタンスは__init__が実行される前の未初期化のインスタンスである点に注意。__new__がClassNameのインスタンスを返した場合に限りClassName.__init__が呼び出される。
また 公式ドキュメント の __new__ についての説明から引用すると
クラス cls の新しいインスタンスを作るために呼び出されます。
__new__()は静的メソッドで (このメソッドは特別扱いされているので、明示的に静的メソッドと宣言する必要はありません)、インスタンスを生成するよう要求されているクラスを第一引数にとります。残りの引数はオブジェクトのコンストラクタの式 (クラスの呼び出し文) に渡されます。__new__()の戻り値は新しいオブジェクトのインスタンス (通常は cls のインスタンス) でなければなりません。
(中略)
__new__()が cls のインスタンスを返した場合、__init__(self[, ...])のようにしてインスタンスの__init__()が呼び出されます。このとき、 self は新たに生成されたインスタンスで、残りの引数は__new__()に渡された引数と同じになります。
__new__()が cls のインスタンスを返さない場合、インスタンスの__init__()メソッドは呼び出されません。
クラスに __metaclass__ という属性があると、type のインスタンス化のかわりに実行される
先ほど、クラスを定義すると内部的には type('クラス名', ...) が実行されると書いたが、クラスに __metaclass__ という属性があるとこの内部的に実行される処理を別の関数で置き換えることができる。
__metaclass__ を利用するケースとしては、type を継承したクラスを用意し、別のクラスの __metaclass__ に指定するというのが一番自然である。
あまり実用的ではないけど、サンプル。
class MyMetaClasss(type): def __new__(cls, name, bases, attrs): attrs['foo'] = 'metacls was here' return type.__new__(cls, name, bases, attrs) class MyClass(object): __metaclass__ = MyMetaClasss def bar(self): print self.__dict__ my_class = MyClass() print my_class.foo # 'metacls was here'
コードによってはメタクラスの第一引数は mcs と書いたり、attrs でなく dict と書いている。
また、__new__ 内では type.__new__ を呼んでいるコードと super(MyMetaClass, cls).__new__ というように親クラスを呼ぶように書いているコードと両方見られた。
(挙動は変わらないはず。変わるとしたらどういったケースか...?)
リファレンス
公式より
その他、参考にさせていただいた記事