pythonほぼ初心者でなので、まずはクラス(の癖のようなもの)を簡単なコードで調べてみる。
class sampleCls():
pass
空っぽのクラスを定義。
この状態でクラスのメンバーを出力してみると、
from pprint import pprint
class sampleCls():
pass
pprint (inspect.getmembers(sampleCls))
実行すると、
[('__doc__', None), ('__module__', '__main__')]
こんな感じに。
1行追加(passの代わりに)して。
class sampleCls():
abc = 111
pprint (inspect.getmembers(sampleCls))
実行すると、
[('__doc__', None), ('__module__', '__main__'), ('abc', 111)]
こんな感じに。
クラス定義を通り過ぎたあと、クラスオブジェクトが生成されている。
クラスオブジェクトは変更することができる。
まだインスタンスを生成しないで、クラスオブジェクトを変更してみる。
class sampleCls():
abc = 111
#sampleCls.def = 222
sampleCls.efg = 222
pprint (inspect.getmembers(sampleCls))
[('__doc__', None), ('__module__', '__main__'), ('abc', 111), ('efg', 222)]
こんな感じで、変更できる。
(sampleCls.defは、defが予約語でエラーになる(;´∀`))
この状態でこのクラスのインスタンスを生成すると、
class sampleCls():
abc = 111
sampleCls.efg = 222
i = sampleCls()
pprint (inspect.getmembers(sampleCls))
print "------------------------------"
pprint (inspect.getmembers(i))
[('__doc__', None), ('__module__', '__main__'), ('abc', 111), ('efg', 222)]
------------------------------
[('__doc__', None), ('__module__', '__main__'), ('abc', 111), ('efg', 222)]
と言う感じで、後から追加したefgもインスタンスに入っている。
因みにクラスオブジェクトから変数を削除することもできる。
class sampleCls():
abc = 111
sampleCls.efg = 222
pprint (inspect.getmembers(sampleCls))
print "------------------------------"
del sampleCls.abc
pprint (inspect.getmembers(sampleCls))
実行すると、
[('__doc__', None), ('__module__', '__main__'), ('abc', 111), ('efg', 222)]
------------------------------
[('__doc__', None), ('__module__', '__main__'), ('efg', 222)]
消えている。この辺はJavaScriptと同じような感じ。
コンストラクタってないの
下記のサイトに、pythonには、コンストラクタって、ないとある。
あと、本にもそのようなことが書いてあった。*1
コンストラクタの定義としては、メモリ確保もコンストラクタでやると言うことらしい。
プログラムを組む立場からすると、メモリ確保は言語に組み込まれているので、インスタンスが初期化できるんで、__init__を一般的に言うコンストラクタと考えても良いかなとも思った。
__new__に関しては、ちょっと違うようで、次にもう少し書いてみようかと。
__new__ってなんだろう
まず簡単なコードを書いてみる。
class sampleCls(object):
abc = 111
print "chkpnt10"
def __new__(cls):
cls.efg = 777
print "chkpnt20"
return super(sampleCls, cls).__new__(cls)
def __init__(self):
self.hij = 999
print "chkpnt30"
pprint(dir(sampleCls))
これを実行すると、
chkpnt10
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'abc']
chkpnt10は出力されているけど、chkpnt20とchkpnt30は表示されない。
クラスオブジェクトのダンプにも、efgもhijも入っていない。
インスタンスを作るとどうなるか?
class sampleCls(object):
abc = 111
print "chkpnt10"
def __new__(cls):
cls.efg = 777
print "chkpnt20"
return super(sampleCls, cls).__new__(cls)
def __init__(self):
self.hij = 999
print "chkpnt30"
i = sampleCls()
pprint (inspect.getmembers(sampleCls))
pprint (inspect.getmembers(i))
chkpnt10
chkpnt20
chkpnt30
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'abc',
'efg']
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'abc',
'efg',
'hij']
chkpnt10、chkpnt20、chkpnt30の順に出力されている(コードが動いている)。
クラスオブジェクトには、abc、efgが入っているけど、hijは入っていない。インスタンスオブジェクトにはabc、efg、hijが全て入っている。
__new__の第一引数はクラスオブジェクトで、上記のコードでは、efgを設定しているので、結果的にクラスオブジェクトにefgが追加される。
super(sampleCls, cls).__new__(cls)
で、クラスオブジェクトからインスタンスオブジェクトを生成して、復帰値として返している。
__new__の復帰値は、そのクラスのインスタンスオブジェクトにする。もし復帰値がそれ以外の場合、クラスオブジェクトによるインスタンスオブジェクトは生成されなくなる。
__new__で生成したインスタンスオブジェクトが第一引数として__init__が呼び出される。
なので、__new__はクラスオブジェクトからインスタンスオブジェクトを生成するためのもの。
上記のコードように、__new__をオーバーライドするとインスタンスオブジェクトを生成する元になるクラスオブジェクトを変更することができ、この変更は、生成するインスタンスオブジェクトにも反映される。
上記のことから__new__はコンストラクタではないなと、自分は思ってしまった。
因みに、クラスには、
・type (新スタイルクラス) と
・class object (旧スタイルクラス)
と言うものがあり、上記のサンプルは、新スタイルクラスになる。らしい。
DataNitroをインストールした時にデフォルトでインストールされるpythonを使っているので、上記は2.7で実行したもの。
2.7だと、(オーバーライドした?)__new__を呼び出すためには、objectとかを継承する必要がある。*2 *3
(3.0以降はデフォルトでobjectを継承するので、クラスを継承する指定は不要になったとのこと。)
ここ何日かpythonを調べてみて、プログラムで結構細かいコントロールができる仕組みがあることもあって、クラスを中心にしても、何かもやもやした感じが残ったまま。次から次へと調べることが・・・。
ただ、嫌いではないですね。やり込むと、良い意味でハマるかも知れない。
余談ですが、printって、3.0以降は、文から式に変わったり、括弧が付けないといけなくなったりで、実装レベルでも影響がある変更があったんですね。
ライブラリのインタフェースとかも変わっているみたいだし。
暫く、pythonの勉強は継続するかな。
*1. 恐らく、__init__がコンストラクタって書いてある本もあるのでしょう。むしろその方が多い?
*2. objectを継承と、__new__の呼び出しとの因果関係を調べようと思って、属性値で__new__の呼び出しがコントロールされるのかと調べたけど、調べきれなかった。
*3. typeを継承しても、__new__が呼び出される。ただ、この場合、メタクラスとかの関係で、ちょっと意味合いが違うのかも知れない。typeについてもこれからの調査課題。