Implementation of multiple inheritance

class A(object) :

    def out(self) :
        print("Class A methods")

class B(object) :

    def out(self) :
        print("Class B methods")

class C(A, B) :
    pass

c = C()
# print the call path order of class C (note the class name.__mro__)
print(C.__mro__)
c.out()
Copy the code


Running results:

(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
AClass methodCopy the code


Try changing the inheritance order of class C to B,A

class C(B, A) :
    pass
Copy the code

As a result,

(<classmain.C'>, <classmain.B'>, <classobject'>, <classmain.A'>)BClass methodCopy the code


If class C overrides the out() method, the out() method of class C will be executed

class C(B, A) :
    
    def out(self) :
        print("Class C methods")
Copy the code

The results are as follows:

(<classmain.C'>, <classmain.B'>, <classobject'>, <classmain.A'>)CClass methodCopy the code


Understand the MRO

The new class can either get the MRO of the class directly from the.__mro__ class name or from the.mro() class name. The old class has no MRO attributes or MRO () methods.

Method Resolution Order (MRO). It is mainly used to determine the invocation path of methods and attributes in multiple inheritance.

  • When searching for a method, it is based onmro()The output results are searched from left to right
  • If found, the method is executed directly in the current class, not searched
  • If no method is found, it will search for the corresponding method in the next class
  • If the last class has not found a method, the program reports an error

The MRO sequence is based on the C3 algorithm in Python and you can explore it if you are interested.


New class and old class

In earlier versions of Python, all classes did not have a common ancestor object. Defining a class without explicitly specifying its ancestor was interpreted as an old-style class, for example:

class oldA:  
	pass

class oldB:
    pass
Copy the code

OldA and oldB are both old-style classes.

In Python 2.x, old-style classes are preserved for backward compatibility. A new class in this version must explicitly inherit from Object or another new class:

class NewA(object) :  
    pass

class NewB(NewA) :  
    pass
Copy the code

Obviously, both of these classes are new classes.

In Python 3.x, the concept of old-style classes is no longer retained. Thus, classes that do not inherit from any other class implicitly inherit from Object.


The use of super ()

The super() function is a method used to call the parent (superclass).

Super () is used to solve the problem of multiple inheritance. Calling a superclass method directly from the class name is fine when using single inheritance, but when using multiple inheritance, there are problems with lookup order (MRO), repeated calls (diamond inheritance), and so on.


Single inheritance

Call by the parent class name

""" Single inheritance calls """ using the parent class name

class Parent(object) :

    def eat(self) :
        print("\tparent - Love to eat")


class Son1(Parent) :


    def eat(self) :
        print("son1 --- eat()")
        Parent.eat(self)
        print("\tson1 -- Loves vegetables \n")
	

class Son2(Parent) :


    def eat(self) :
        print("son2 --- eat()")
        Parent.eat(self)
        print("\tson2 - Likes fruit \n")


def main() :
    s1 = Son1()
    s2 = Son2()

    s1.eat()
    s2.eat()

    
if __name__ == '__main__':
	main()
Copy the code


The results

Son1 loves vegetables. Son2 loves fruitsCopy the code


Use the super ()

Use of super() in single inheritance

class Parent(object) :

    def eat(self) :
        print("\tparent - Love to eat")


class Son1(Parent) :


    def eat(self) :
        print("son1 --- eat()")
        super().eat()
        print("\tson1 -- Loves vegetables \n")


class Son2(Parent) :


    def eat(self) :
        print("son2 --- eat()")
        super().eat()
        print("\tson2 - Likes fruit \n")


def main() :
    s1 = Son1()
    s2 = Son2()

    s1.eat()
    s2.eat()


if __name__ == '__main__':
    main()
Copy the code


Running results:

Son1 loves vegetables. Son2 loves fruitsCopy the code


You can see that using the superclass name in single inheritance results in the same result as calling the superclass method with super().


Multiple inheritance

So again, I’ve got a GrandSon class, taking Son1, Son2. Make eat() behave like its parent.

Call by the parent class name

Use of superclass names in multiple inheritance

class Parent(object) :

    def eat(self) :
        print("\tparent - Love to eat")


class Son1(Parent) :


    def eat(self) :
        print("son1 --- eat()")
        Parent.eat(self)
        print("\tson1 -- Loves vegetables \n")
	

class Son2(Parent) :


    def eat(self) :
        print("son2 --- eat()")
        Parent.eat(self)
        print("\tson2 - Likes fruit \n")


class Grandson(Son1, Son2) :
	
    def eat(self) :
        print("grandson --- eat()")
        # super().eat()
        Son1.eat(self)
        Son2.eat(self)
        print("\ Tgrandson - Love snacks")


def main() :
    # s1 = Son1()
    # s2 = Son2()

    # s1.eat()
    # s2.eat()

    g = Grandson()
    g.eat()


if __name__ == '__main__':
    main()
Copy the code


The results

What was the man's first job, taking a grandson's first job at the office, taking a grandson's first job at the office, taking a grandson's first job at the office, taking a grandson's first job at the office, taking a grandson's first job at the office, taking a grandson's first job at the office Love to eat snacksCopy the code

The result shows that the Parent’s eat() method is called multiple times.


Use the super ()

Use of super() in multiple inheritance

class Parent(object) :

    def eat(self) :
        print("\tparent - Love to eat")


class Son1(Parent) :


    def eat(self) :
        print("son1 --- eat()")
        super().eat()
        print("\tson1 -- Loves vegetables \n")


class Son2(Parent) :


    def eat(self) :
        print("son2 --- eat()")
        super().eat()
        print("\tson2 - Likes fruit \n")


class Grandson(Son1, Son2) :
	
    def eat(self) :
        print("grandson --- eat()")
        super().eat()
        print("\ Tgrandson - Love snacks")


def main() :
    g = Grandson()
    g.eat()


if __name__ == '__main__':
    main()
Copy the code


The results

What was the man's first job? What was the man's first job? What was the man's first job? What was the man's first jobCopy the code

You can see that using super() in multiple inheritance is not called repeatedly.


Grandson’s eat() only uses Son1’s eat() and Son2’s eat(). How do you do that?

Call by the parent class name

""" Use of parent class names ""

class Parent(object) :

    def eat(self) :
        print("\tparent - Love to eat")


class Son1(Parent) :

    def eat(self) :
        print("son1 --- eat()")
        Parent.eat(self)
        print("\tson1 -- Loves vegetables \n")
	

class Son2(Parent) :

    def eat(self) :
        print("son2 --- eat()")
        Parent.eat(self)
        print("\tson2 - Likes fruit \n")


class Grandson(Son1, Son2) :
	
    def eat(self) :
        print("grandson --- eat()")
        Son1.eat(self)
        # Son2.eat(self)
        print("\ Tgrandson - Love snacks")


def main() :
    print(Grandson.mro())
    g = Grandson()
    g.eat()


if __name__ == '__main__':
    main()
Copy the code


The results are as follows

[<class '__main__.Grandson'>, 
 <class '__main__.Son1'>, 
 <class '__main__.Son2'>, 
 <class '__main__.Parent'>, 
 <class 'object'>]grandson --- eat(a)son1 --- eat(a)parent- love to eatson1-- I like vegetablesgrandson-- Love snacks [Finished in 0.1s]
Copy the code


Super () call

Use of super()

class Parent(object) :

    def eat(self) :
        print("\tparent - Love to eat")


class Son1(Parent) :

    def eat(self) :
        print("son1 --- eat()")
        super().eat()
        print("\tson1 -- Loves vegetables \n")


class Son2(Parent) :

    def eat(self) :
        print("son2 --- eat()")
        super().eat()
        print("\tson2 - Likes fruit \n")


class Grandson(Son1, Son2) :
	
    def eat(self) :
        print("grandson --- eat()")
        super(Son1, self).eat()
        # Son1.eat(self)
        # Son2.eat(self)
        print("\ Tgrandson - Love snacks")


def main() :
    print(Grandson.mro())
    g = Grandson()
    g.eat()


if __name__ == '__main__':
    main()
Copy the code


The results are as follows

[<class '__main__.Grandson'>, 
 <class '__main__.Son1'>, 
 <class '__main__.Son2'>, 
 <class '__main__.Parent'>, 
 <class 'object'>]grandson --- eat(a)son2 --- eat(a)parent- love to eatson2-- Love fruitgrandson-- Love snacks [Finished in 0.1s]
Copy the code

However, it turns out that super(Son1, self).eat() calls Son2’s eat() method.

It’s because of MRO, so when I call super(Son1, self).eat(), I take Son1 from Grandson’s MRO method and look for it in the table, and then super() is the next one in the list, so here’s Son2, Super ().eat() is used in son2.eat (), which is the next Parent in Son2’s MRO method parsing order.

So in Grandson’s case super(Son1, self).eat() is called son2.eat ().

If super(Son2, self).eat() is called parent-.eat (). The results are as follows

[<class '__main__.Grandson'>, 
 <class '__main__.Son1'>, 
 <class '__main__.Son2'>, 
 <class '__main__.Parent'>, 
 <class 'object'>]grandson --- eat(a)parent- love to eatgrandson-- Like to eat snacksCopy the code

Therefore, son1.eat () can only be called as a superclass name and only inherits Son1’s properties. If you don’t know who is calling super(), print it

Class name. Mro (), compare mrO method parse order table, at a glance.


conclusion

  • Method Resolution Order (MRO). It is mainly used to determine the invocation path of methods and attributes in multiple inheritance

  • Legacy classes that have no common object ancestor and do not explicitly specify their ancestor.

  • New classes, which explicitly inherit object in Python 2.x or other new classes, implicitly inherit object in Python3.x

  • Super ().method() is no worse for single inheritance than.method(self)

  • The super() method ensures that methods of each parent class are executed only once, whereas methods using class names cause methods to be executed multiple times, as shown in the previous output


The source code

The source code has been uploaded to Gitee PythonKnowledge: PythonKnowledge Repository.

✍ code word is not easy, but also hope you heroes support ❤️.


The public,

Create a new folder X

Nature took tens of billions of years to create our real world, while programmers took hundreds of years to create a completely different virtual world. We knock out brick by brick with a keyboard and build everything with our brains. People see 1000 as authority. We defend 1024. We are not keyboard warriors, we are just extraordinary builders of ordinary world.