LoginSignup
13
15

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