クラスの継承
これまでの例で、Artistクラスや、Priceクラスはアトリビュートやメソッドを定義しなかったのに、いくつかは既に定義済みとなっていることがわかったはずです。でも、それはなぜなんでしょう?
実はクラスは、objectクラスのアトリビュートを持っています。
code:python
> dir(object)
'__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__' >>
次の2つのクラス定義はまったく同じことになります。
code: 0801_class_definition.py
class Price:
pass
c1 = Price()
print(dir(c1))
class Price(object):
pass
c1 = Price()
print(dir(c1))
ここで改めて、Pythonのクラス定義の構文を見てみましょう。
class クラス名:
クラス定義
class クラス名():
クラス定義
class クラス名(親クラス):
クラス定義
class クラス名(親クラス1, 親クラス2, ...):
クラス定義
ひとつのファイルに複数のクラスを定義することができます。
親クラスで指定したクラスの定義内容が継承(inheritance) されて、自分が’定義するクラスで利用することができます。
はじめの2つの構文例では、親クラスを明示的に与えていません。このときは、objectが指定されているものとして動作します。
親クラスは継承する基となるクラスで、ベースクラス(Base Class) や スーパークラス(Super Class) といいます。
このとき、継承したクラスは、親クラスのサブクラス(Subclass)となります。サブクラスは、派生クラス(Derived Class) ともいいます。
複数のクラスを継承することを多重継承(Multiple Inheritance) といい、Python でもサポートされています。
code:0802_class_multiple_inheritance.py
class Father:
id='father'
class Mother:
id='mother'
class Child(Father, Mother):
pass
print(Child.id)
# Output: 'father'
多重継承での参照順序は、クラス定義時に指定した左から右への順序になります。
継承を調べる
クラスがどのクラスを継承しているか調べることもできます。
code: 0803_class_method_resolution_order.py
class Father:
id='father'
class Mother:
id='mother'
class Child(Father, Mother):
pass
print(Child.id)
print(issubclass(Child,Mother))
print(issubclass(Child,Father))
# MRO (Method Resolution Order)
print(Child.__mro__)
issubclass(x, y) はクラスx が クラスy のサブクラスのときに真値を返します。
__mro__ には、アトリビュートを参照するときの検索順序が設定されています。
code: bash
$ python 0803_class_method_resolution_order.py
father
True
True
(<class '__main__.Child'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>)
親クラスのメソッドを呼び出す
継承した親クラスのメソッドを呼び出すこともできます。
code: 0804_class_call_superclass.py
class Family:
def __init__(self, name):
self.name = name
# Father class inherited from Family
class Person(Family):
def __init__(self, name, age):
Family.__init__(self, name) # super().__iit__(name) と同じ
self.age = age
f = Person("Freddie", 48)
print(f.name)
print(f.age)
このようにクラスは、その定義を拡張することが容易になります。
__new__() と __init__() メソッド
これまで特殊メソッド__init__() は、インスタンスオブジェクトを生成時に実行されてコンストラクタとして機能すると説明してきました。
クラスからインスタンスオブジェクトを生成するときには次のことが起きています。
親クラスの__call__() が呼び出され、次のことが実行されます
__new__() の実行
__init__() の実行
クラスに__new__() や__init__()が定義されていないときは、親クラスを参照します。
クラス継承している場合では、必ずしも__init__() は実行されるとは限りません。
これに対して __new__()は __init__() よりも前に、必ず呼び出されます。
この2つの特殊メソッドは次のような違いがあります。
__new__(cls):
インスタンスオブジェクトが生成される前に呼ばれる
オブジェクト(self)をインスタンス化する
第一引数にクラスオブジェクト(cls)を受け取る
__init__(self):
インスタンスオブジェクトが生成された後に呼ばれる
オブジェクト(self) を初期化する
第一引数にインスタンスオブジェクト(self) を受け取る
まずは、次の例を見てみましょう。
code: 0805_class_new_and_init.py
class Person:
def __new__(cls):
print('new is called')
def __init__(self):
print('init is called')
obj = Person()
これを実行すると、次のように __init__() が呼び出されていないことが確認できます。
code: bash
$ python 0805_class_new_and_init.py
new is called
クラス定義で継承していない場合でも objectクラスが指定されていると説明しました。
そこで次のように親クラスの特殊メソッドを呼び出すようにしてみます。
code: 0806_class_new_and_init_with_call_superclass.py
class Person(object):
def __new__(cls):
print('new is called')
return super().__new__(cls)
def __init__(self):
print('init is called')
super().__init__()
obj = Person()
こんどは、インスタンスオブジェクト生成時に、Personクラスの __init__() が呼び出されました。
code: bash
$ python 0806_class_new_and_init_with_call_superclass.py
new is called
init is called
重要なことは次のとおりです。
クラスの __new__() は必ず実行される
クラスの__init__() は実行されない場合がある
__new__() で何も返さないときは実行されない