はじめに
Pythonの仕様なので、こういうものだと覚える!
クラスの定義は以下の形
class クラス名: def メソッド名(self, ...): ... def メソッド名(self, ...): ...
インスタンス生成
ソース
class myclass: def __init__(self): pass a = myclass() print(type(a))
実行結果
<class '__main__.myclass'>
クラスオブジェクト
クラスもオブジェクト
クラスを定義するとクラスオブジェクトが生成される
クラスオブジェクトの代入可能
ソース
class myclass: def __init__(self): pass a = myclass b = a() print(type(b))
実行結果
<class '__main__.myclass'>
属性指定
class myclass: def __init__(self): self.hoge = "hoge" a = myclass() print(a.hoge)
実行結果
hoge
属性削除
ソース
class myclass: def __init__(self): self.hoge = "hoge" a = myclass() print(a.hoge) del a.hoge print(a.hoge)
実行結果
Traceback (most recent call last): File "/Users/pike/PycharmProjects/Study/c.py", line 8, in <module> print(a.hoge) AttributeError: 'myclass' object has no attribute 'hoge' hoge
メソッド
クラスに定義された関数
selfを引数として持つ。selfは生成されたインスタンス自身
ソース
class A: def m1(self): print("A.m1") self.m2("m1 messeage") def m2(self, mes): print("A.m2") print(mes) a = A() a.m1()
実行結果
A.m1 A.m2 m1 messeage
コンストラクタ
インスタンス生成時に呼び出されるメソッド。名前は"__init__"にする
ソース
class A: def __init__(self): print("A.m1") a = A()
実行結果
A.m1
デストラクタ
インスタンス削除時に呼び出されるメソッド。名前は"__del__"にする
ソース
class A: def __del__(self): print("del A") a = A() del a
実行結果
del A
継承
既存のクラスの派生クラスの定義
定義時に既存クラスを指定。これを基底クラスと呼ぶ
基底クラス未指定の場合、objectクラスを継承
super()で基底クラスのメソッド呼び出しが可能
ソース
class A: def __init__(self, arg1): self.atr1 = arg1 def m1(self): print("m1") class B(A): def __init__(self, arg1, arg2): super().__init__(arg1) self.atr2 = arg2 def m2(self): print("m2") i = B("hoge", "fuga") print(i.atr1) print(i.atr2) i.m1() i.m2()
実行結果
hoge fuga m1 m2
メソッドのオーバーライド
継承クラス定義と同名のメソッドを定義するとオーバーライドする
ソース
class K(): def m(self): print("m by K") def m2(self): print("m2 by K") class A(K): def m(self): print("m by A") a = A() a.m() a.m2()
実行結果
m by A m2 by K
多重継承
複数クラスの継承
ソース
class A: def m1(self): print("m1") class B: def m2(self): print("m2") class C(A, B): def m3(self): print("m3") a = C() a.m1() a.m2() a.m3()
実行結果
m1 m2 m3
メソッドの検索順が確定できない場合エラー
ソース
class A: def m1(self): print("m1") class B(): def m2(self): print("m2") class C(A, B): def m3(self): print("m3") class D(B, A): def m4(self): print("m4") class E(C, D): def m5(self): print("m5")
実行結果
Traceback (most recent call last): File "/Users/pike/PycharmProjects/Study/a.py", line 17, in <module> class E(C, D): TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
※CはA→B、DはB→A順で検索。EはC→D順で検索するが、CとDが矛盾する為、エラー(内部実装の話。こういうものだと覚える!)
メソッドの検索順はmroメソッドで取得可能
ソース
class A: def m1(self): print("m1") class B(): def m2(self): print("m2") class C(A, B): def m3(self): print("m3") a = C() print(C.mro())
実行結果
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
Cのインスタンスのmroメソッドを参照すると未定義エラーとなる。
ソース
class A: def m1(self): print("m1") class B(): def m2(self): print("m2") class C(A, B): def m3(self): print("m3") a = C() print(a.mro())
実行結果
Traceback (most recent call last): File "/Users/pike/PycharmProjects/Study/a.py", line 14, in <module> print(a.mro()) AttributeError: 'C' object has no attribute 'mro'
インスタンスアロケータ
__new__メソッドのこと。__init__と似てるが、こりゃ難しい。以下URLも参考。
__new__と__init__とメタクラスと - Qiita
インスタンスアロケータ - Python学習講座
__new__の第一引数はクラスオブジェクト(cls)、__new__はインスタンス(self)
ソース
class A(): def __new__(cls): print("A __new__") print(cls) class B(): def __init__(self): print("B __init__") print(self) a = A() aa = A print(aa) print("") b = B() print(b)
実行結果
A __new__ <class '__main__.A'> <class '__main__.A'> B __init__ <__main__.B object at 0x107abcc18> <__main__.B object at 0x107abcc18>
__new__も__init__もインスタンス生成時に呼び出される
__new__の方が先に呼び出される。__new__の戻り値がsuper().__new__(cls)の場合のみ、__init__が実行される
ソース
class A(): def __new__(cls): print("A __new__") return super().__new__(cls) def __init__(self): print("A __init__") class B(): def __new__(cls): print("B __new__") def __init__(self): print("B __init__") a = A() b = B()
実行結果
A __new__ A __init__ B __new__
return super().__new__(cls)とは?→クラスAのインスタンスである!
参考書籍にも以下記載あり
__new__はインスタンス生成する為に呼び出される。戻り値がクラスのインスタンスなら、そのインスタンスの__init__メソッドが実行される
やっと理解できた!
ソース
class A(): print("A super().__new__(cls)") def __new__(cls): print(super().__new__(cls)) return super().__new__(cls) def __init__(self): self.hoge = "A" a = A() print(a)
実行結果
A super().__new__(cls) <__main__.A object at 0x107595cc0> <__main__.A object at 0x107595cc0>
インスタンスアロケータの用途は?→参考書籍やURLを読み、以下と理解した
イミュータブル(変更不可)なクラス(int等)を継承しても、イミュータブルだからメソッドや属性を__init__ではオーバーライドできない(エラーになる)。ただ__new__ならできる。
ソース
class A(int): def __init__(self): self.hoge = "A" a = A() print("test")
実行結果
test
しかし以下コードを実行したが。。。エラーにならない。。。理解を誤ってるのだろう
ソース
class A(int): def __init__(self): self.real = 2 a = A()
実行結果
こういうことかも
__init__はself(インスタンス)を引数にする=既にインスタンス生成後の処理→なのでインスタンス生成前の処理をオーバーライドできない。→__new__ならできる!
intを継承したクラスでインスタンス生を成し、その値が常に2になるクラスを実装する場合
__init__ではできない。ダメ元で以下コードで試す
ソース
""" # intの__init__メソッドのソースコードの内容。passしてるだけ。 def __init__(self, x, base=10): pass """ class A(int): def __init__(self, x, base=10): x = "2" pass a = A("1") print(a)
実行結果
1
__new__でインスタンス生成時の引数を2に変更!
ソース
class A(int): def __new__(cls, s): s = "2" return super().__new__(cls, s) a = A("1") print(a)
実行結果
2