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

后端 未结 5 1339
我寻月下人不归
我寻月下人不归 2021-02-18 13:01

According to the docs on inheritance:

Derived classes may override methods of their base classes. Because methods have no special privileges when calling

相关标签:
5条回答
  • 2021-02-18 13:43
    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"

    0 讨论(0)
  • 2021-02-18 13:49

    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.

    0 讨论(0)
  • 2021-02-18 13:52

    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.

    0 讨论(0)
  • 2021-02-18 14:02

    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!

    0 讨论(0)
  • 2021-02-18 14:02

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

    class Base(object):
    
        def something (self):
            raise ('Not implemented')
    
    0 讨论(0)
提交回复
热议问题