2013年12月22日日曜日

python __new__ と __init__ とコンストラクタ

appengineで__new__がどうたらこうたらというエラーに遭遇したので、今更調べてみました。
classの再定義(Typeオブジェクトの上書き)だけでもやっていけてしまいますしね。

端的に言うと、

__new__ は、オブジェクト生成直前に呼ばれる

__init__ は、オブジェクト生成後の初期化

ということらしい。

本来の「オブジェクト指向」「コンストラクタ」はメモリ確保そのものも含みますが、

http://en.wikipedia.org/wiki/Constructor_(object-oriented_programming)

http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF

日本に蔓延している「オブジェクト指向っぽい何か」は
「Java系オブジェクト指向実装」を源流とするもので、
あの界隈では真のコンストラクトはコンパイラの仕事で、
「コンストラクタ」と呼ばれる機構には、初期化の役目しか残ってません。

故に、少なくとも日本語圏で、
__init__をコンストラクタと呼んでしまう風潮は間違ってはいないでしょう。

以前、「pythonにはnew演算子が無い」というエントリを書きましたが、
これはその延長線上(もしくはそれを遡った根っこ)にあるものですね。

pythonでは、クラス宣言=Typeオブジェクトの生成 であり、
本来の意味でのコンストラクタは、Type.__call__が隠蔽しています。
ということが多分、この辺りに書いてある?
https://github.com/python-git/python/blob/master/Objects/typeobject.c#720

__init__を呼んでるのは、実はobject.__new__の機能だったりするので、
__new__初心者はそこでハマるらしい。
http://stackoverflow.com/questions/9367126/why-is-init-not-called-after-new-sometimes

問題の__new__ですが、慣れるより見習え!、ということで、色々ソースを捜索しました。
google_appengine_SDKはpython達人実装の宝庫です。

http://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/ext/db/djangoforms.py#714

基底クラスの「亜種」を動的に作るのに使ってる場合が多いみたいですね。

とはいえ、python2.4以前では、似たような機構を__metaclass__でやってたらしい。

http://docs.python.jp/2.4/ref/metaclasses.html

python-SDKでもそっちの方の印象が強いです。Modelクラスの小細工に使っていたような。

http://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/ext/db/__init__.py#798

意味は未だに判らない。__new__の方が機構的に「初心者向け」でしょうか。

PHPとかだとevalで強引に出来なくもありませんが、C#/Java族だと逆立ちしても無理ですね。