How do overridden method calls from base-class met

2019-04-03 05:24发布

问题:

According to the docs on inheritance:

Derived classes may override methods of their base classes. Because methods have no special privileges when calling other methods of the same object, a method of a base class that calls another method defined in the same base class may end up calling a method of a derived class that overrides it.

How does that happen? Can someone illustrate this concept with a simple example?

回答1:

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.



回答2:

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.



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

prints "base sub"



回答4:

To illustrate how it works consider these two classes:

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'

The __getattribute__ method is responsible in new-style-classes (like all classes in python3) for the attribute lookup. It is just implemented to print what each lookup does - normally you don't want to and shouldn't implement it yourself. The lookup follows pythons method resolution order (MRO) just if you are really interested.

>>> 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.

So when you want to use eat then it uses Parent.eat in this example. But self.takefrompocket is used from 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.

Here both methods are taken from Parent. Inherited classes don't (generally) interfere with their ancestors!



回答5:

If your child class doesn't implement the method, raise an exception!

class Base(object):

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