継承に関するドキュメント によると:
派生クラスは、基本クラスのメソッドをオーバーライドできます。同じオブジェクトの他のメソッドを呼び出す場合、メソッドには特別な特権がないため、同じ基本クラスで定義された別のメソッドを呼び出す基本クラスのメソッドは、それをオーバーライドする派生クラスのメソッドを呼び出す場合があります。
それはどうして起こりますか?誰かがこの概念を簡単な例で説明できますか?
こちらがリクエストした例です。これはchocolate
を出力します。
_class Base:
def foo(self):
print("foo")
def bar(self):
self.foo()
class Derived(Base):
def foo(self):
print("chocolate")
d = Derived()
d.bar() # prints "chocolate"
_
chocolate
はfoo()
関数をオーバーライドするため、foo
の代わりに文字列Derived
が出力されます。 bar()
はBase
で定義されていますが、Derived
実装ではなくfoo()
のBase
実装を呼び出すことになります。
クラスのインスタンスで属性ルックアップが実行されると、クラスディクショナリおよびその基本クラスのディクショナリが特定の順序で検索されます(適切なメソッドについては、 メソッド解決順序 )を参照してください。見つかったものfirstが呼び出されます。
次のSpam
の例を使用します。
class Spam:
def produce_spam(self):
print("spam")
def get_spam(self):
self.produce_spam()
class SuperSpam(Spam):
def produce_spam(self):
print("super spam")
Spam
は、関数produce_spam
およびget_spam
を定義します。これらはSpam.__dict__
(クラス名前空間)にあります。サブクラスSuperSpam
は、継承により、これらの両方のメソッドにアクセスできます。 SuperSpam.produce_spam
はSpam.produce_spam
に代わるものではありません。名前の検索が'produce_spam'
のインスタンスの1つで行われると、最初に見つかります。
基本的に、継承の結果、サブクラスの属性ルックアップが行われた後、属性がサブクラスのディクショナリに見つからない場合、基本クラスのディクショナリも検索されます。
関数get_spam
が最初に呼び出されたとき:
s = SuperSpam()
s.get_spam()
イベントのシーケンスおおよそは次のようになります。
SuperSpam
s __dict__
でget_spam
を確認します。SuperSpam
s __dict__
にはないため、その基本クラス(mro
チェーン)の辞書を調べてください。Spam
はmro
チェーンの次なので、Spam
の辞書でget_spam
が見つかります。これで、produce_spam
がget_spam
の本文でself.produce_spam
で検索されると、シーケンスがはるかに短くなります。
SuperSpam
の(self
)__dict__
でproduce_spam
を探します。produce_spam
が最初に__dict__
にあるため、フェッチされます。
class Base():
def m1(self):
return self.m2()
def m2(self):
return 'base'
class Sub(Base):
def m2(self):
return 'sub'
b = Base()
s = Sub()
print(b.m1(), s.m1())
「base sub」を出力します
それがどのように機能するかを説明するために、次の2つのクラスを検討します。
class Parent(object):
def eat(self):
print("I don't want to eat that {}.".format(self.takefrompocket()))
def takefrompocket(self):
return 'Apple'
def __getattribute__(self, name):
print('Looking for:', name)
method_to_use = object.__getattribute__(self, name)
print('Found method:', method_to_use)
return method_to_use
class Child(Parent):
def takefrompocket(self):
return 'salad'
__getattribute__
メソッドは、(python3のすべてのクラスと同様に)属性ルックアップの新しいスタイルのクラスを担当します。 print
に実装されているだけで、各ルックアップが何を行うか-通常はしたくないので、自分で実装するべきではありません。ルックアップはpythons method resolution order(MRO) に従います。
>>> some_kid = Child()
>>> some_kid.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Child object at 0x0000027BCA4EEA58>>
Looking for: takefrompocket
Found method: <bound method Child.takefrompocket of <__main__.Child object at 0x0000027BCA4EEA58>>
I don't want to eat that salad.
したがって、eat
を使用する場合、この例ではParent.eat
を使用します。ただし、Child
からはself.takefrompocket
が使用されます。
>>> some_parent = Parent()
>>> some_parent.eat()
Looking for: eat
Found method: <bound method Parent.eat of <__main__.Parent object at 0x0000027BCA4EE358>>
Looking for: takefrompocket
Found method: <bound method Parent.takefrompocket of <__main__.Parent object at 0x0000027BCA4EE358>>
I don't want to eat that Apple.
ここでは両方のメソッドがParent
から取得されています。継承されたクラスは、(一般的に)祖先を妨害しません!