13
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

クラスについてちょっと調べてみた

Posted at

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についてもこれからの調査課題。

13
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?