How do overridden method calls from base-class methods work? How do overridden method calls from base-class methods work? python-3.x python-3.x

How do overridden method calls from base-class methods work?


Here's the example you requested. This prints 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"

The string chocolate is printed instead of foo because Derived overrides the foo() function. Even though bar() is defined in Base, it ends up calling the Derived implementation of foo() instead of the Base implementation.


How does it work?

When an attribute look-up is performed on an instance of the class, the class dictionary and the dictionaries of its base classes are searched in a certain order (see: Method Resolution Order) for the appropriate method. What is found first is going to get called.

Using the following Spam example:

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 defines the functions produce_spam and get_spam. These live in its Spam.__dict__ (class namespace). The sub-class SuperSpam, by means of inheritance, has access to both these methods. SuperSpam.produce_spam doesn't replace Spam.produce_spam, it is simply found first when the look-up for the name 'produce_spam' is made on one of its instances.

Essentially, the result of inheritance is that the dictionaries of any base classes are also going to get searched if, after an attribute look-up on the sub-class is made, the attribute isn't found in the sub-class's dictionary.

When the function get_spam is first invoked with:

s = SuperSpam()s.get_spam()

the sequence of events roughly goes like this:

  • Look into SuperSpams __dict__ for get_spam.
  • Since it isn't found in SuperSpams __dict__ look into the dictionaries of it's base classes (mro chain).
  • Spam is next in the mro chain, so get_spam is found in Spam's dictionary.

Now, when produce_spam is looked up in the body of get_spam with self.produce_spam, the sequence is much shorter:

  • Look into SuperSpam's (self) __dict__ for produce_spam.
  • Find it, get it and call it.

produce_spam is found in the __dict__ first so that gets fetched.


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

prints "base sub"