Подтвердить что ты не робот

Как работают переопределенные вызовы методов из методов базового класса?

В соответствии с docs о наследовании:

Производные классы могут переопределять методы их базовых классов. Поскольку методы не имеют особых привилегий при вызове других методов одного и того же объекта, метод базового класса, который вызывает другой метод, определенный в том же базовом классе, может в конечном итоге вызвать метод производного класса, который переопределяет его.

Как это происходит? Может ли кто-нибудь проиллюстрировать эту концепцию простым примером?

4b9b3361

Ответ 1

Вот пример, который вы запросили. Это печатает 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, потому что Derived переопределяет функцию foo(). Хотя bar() определяется в Base, он заканчивает вызов Derived реализации foo() вместо реализации Base.

Ответ 2

Как это работает?

Когда для экземпляра класса выполняется поиск атрибута, словарь словаря и, поиск словарей его базовых классов осуществляется в определенном порядке (см. Порядок разрешения метода) для соответствующего метода. То, что обнаружено первым, будет вызвано.

Используя следующий пример 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' выполняется в одном из его экземпляров.

По сути, результатом наследования является то, что словари любых базовых классов также будут искать, если после того, как будет создан атрибут для подкласса, этот атрибут не будет найден в подклассе словарь.

Когда функция get_spam сначала вызывается с помощью:

s = SuperSpam()
s.get_spam()

последовательность событий грубо идет следующим образом:

  • Посмотрите SuperSpam __dict__ для get_spam.
  • Так как он не найден в SuperSpam __dict__, загляните в словари его базовых классов (mro chain).
  • Spam следующий в цепочке mro, поэтому get_spam находится в словаре Spam.

Теперь, когда produce_spam просматривается в теле get_spam с self.produce_spam, последовательность намного короче:

  • Посмотрите SuperSpam __dict__ для produce_spam.
  • Найдите его, получите и назовите его.

produce_spam сначала находится в __dict__, чтобы получить выбор.

Ответ 3

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())

печатает "базовый суб"

Ответ 4

Чтобы проиллюстрировать, как это работает, рассмотрите эти два класса:

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 порядок разрешения метода (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. Но self.takefrompocket используется из Child.

>>> 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. Унаследованные классы не (обычно) мешают их предкам!

Ответ 5

Если ваш дочерний класс не реализует этот метод, создайте исключение!

class Base(object):

    def something (self):
        raise ('Not implemented')