web-dev-qa-db-ja.com

基本クラスのメソッドからのオーバーライドされたメソッド呼び出しはどのように機能しますか?

継承に関するドキュメント によると:

派生クラスは、基本クラスのメソッドをオーバーライドできます。同じオブジェクトの他のメソッドを呼び出す場合、メソッドには特別な特権がないため、同じ基本クラスで定義された別のメソッドを呼び出す基本クラスのメソッドは、それをオーバーライドする派生クラスのメソッドを呼び出す場合があります。

それはどうして起こりますか?誰かがこの概念を簡単な例で説明できますか?

23
wow yoo

こちらがリクエストした例です。これは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"
_

chocolatefoo()関数をオーバーライドするため、fooの代わりに文字列Derivedが出力されます。 bar()Baseで定義されていますが、Derived実装ではなくfoo()Base実装を呼び出すことになります。

29
IanPudney

どのように機能しますか?

クラスのインスタンスで属性ルックアップが実行されると、クラスディクショナリおよびその基本クラスのディクショナリが特定の順序で検索されます(適切なメソッドについては、 メソッド解決順序 )を参照してください。見つかったもの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_spamSpam.produce_spamに代わるものではありません。名前の検索が'produce_spam'のインスタンスの1つで行われると、最初に見つかります。

基本的に、継承の結果、サブクラスの属性ルックアップが行われた後、属性がサブクラスのディクショナリに見つからない場合、基本クラスのディクショナリも検索されます。

関数get_spamが最初に呼び出されたとき:

s = SuperSpam()
s.get_spam()

イベントのシーケンスおおよそは次のようになります。

  • SuperSpams __dict__get_spamを確認します。
  • SuperSpams __dict__にはないため、その基本クラス(mroチェーン)の辞書を調べてください。
  • Spammroチェーンの次なので、Spamの辞書でget_spamが見つかります。

これで、produce_spamget_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」を出力します

8
Terry Jan Reedy

それがどのように機能するかを説明するために、次の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から取得されています。継承されたクラスは、(一般的に)祖先を妨害しません!

1
MSeifert