[Python] What is inherited by multiple inheritance?

About this article

I want to override Django and change its functionality, but I don't know how to do it ... In the first place, I couldn't read the multi-joint code, so I studied. In this article, I would like to organize the mechanism and usage of multiple inheritance of Python, and finally play with it.

What is inheritance?

Inheritance is the inheritance of the functionality of a class.

#Base class
class Coffee(object):
    def __init__(self, color, fragrance, price, taste, elapsed_time):
        self.color = color
        self.fragrance = fragrance
        self.price = price
        self.satisfaction = ((taste + fragrance) * price) - elapsed_time

    def drink(self):
        print(f'Satisfaction{self.satisfaction}Drink Hoffy of the point')


coffee = Coffee('brown', 10, 200, 10, 300)
coffee.drink() # <---Drink Hoffy with 3700 satisfaction

#Inherit (derived class)
class Coffee2(Coffee): 
    pass # <-Has exactly the same functionality as coffee

class Nihonsyu(Coffee2):
    pass # <-Has exactly the same function as Coffee2

Inherited classes can add functionality by adding or overriding methods. Inheriting one class like class Coffee2 (Coffee): is called ** single inheritance **.

Inheritance order

In the case of single inheritance as above, you can find out by simply tracing the inheritance source. Nihonsyu <- Coffee2 <- Coffee

Class inheritance searches for methods in inheritance order and inherits those methods. By the way, when multiple parent classes have the same method name, only the method of the first inherited parent class can be inherited. However, if super () is defined in the method of the parent class, all the functions of the parent class and the parent class can be inherited. Either way, if you want to override multiple inheritance methods, you have to understand what methods are inherited. To do that, it seems necessary to know the order of inheritance of the parent class.

MRO will tell you

MRO is called Method Resolution Order. This is the order in which methods are searched when inheriting a class. Python's base class ʻobject` has a method called mro, which you can use to find out the MRO, so try it with the code above.


print(Nihonsyu.__mro__)
# (<class '__main__.Nihonsyu'>, <class '__main__.Coffee2'>, <class '__main__.Coffee'>, <class 'object'>)

It was displayed as (<class'__ main__. Nihonsyu'>, <class'__ main__. Coffee2'>, <class'__ main__. Coffee'>, <class'object'>). As you can see, I was able to display the search order of the methods.

What is multiple inheritance?

Multiple inheritance is the state when two or more classes are passed as arguments of the class.

class A:
    def hello(self):
        print('Hello from A')

class B(A):
    pass

class C(A):
    def hello(self):
        print('Hello from C')

class D(B, C): # <-Multiple inheritance
    pass

D is a multi-inherited class, what will be output when I execute the hello method on an instance of D?

d = D()
d.hello()
# Hello from C

Hello from C is output. In other words, it inherits the method of class C. Since it is class D (B, C), I wondered if class B has priority and the Hello from A of class A, which inherits the entire class B, is output. Why? ..

Order of multiple inheritance

It seems that there are depth-first and width-first algorithms that determine the order of multiple inheritance, but Python seems to use an algorithm that is neither C linear type. I will omit this because it will be a deep story, but it seems that the MRO changes depending on the structure of multiple inheritance, and the search is less redundant. For the time being, you can find out the MRO using __mro__, so let's check it.

MRO will tell you

Try it with the multiple inheritance code above.


print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

It became (<class'__ main__. D'>, <class'__ main__. B'>, <class'__ main__. C'>, <class'__ main__. A'>, <class'object'>).

Precautions for __init__ when inheriting multiple times

When multiple inheritance is performed, methods etc. are inherited according to MRO, but it seems that methods with the same name can only inherit those of the first inherited class. It would be nice if the method with the same name does not exist, but since __init__ almost always exists, it seems that it is necessary to firmly call init of the class you want to inherit with super () and perform the initialization process properly. ..

Also, the __init__ in the code class C below seems to override the __init__ of the ʻobject class at first glance, but at the time of multiple inheritance, the methods follow the order of MRO. It seems to override init of Bnext toC` because it seems to override.

** Be sure to remember that the order of methods overridden by super () changes during multiple inheritance **


class B:
    def __init__(self):
        self.b_value = 'B'
        print('class B init')
        super().__init__()

class C:
    def __init__(self):
        self.c_value = 'C'
        print('class C init')
        super().__init__()

class D(C, B):
    def __init__(self):
        print('class D init')
        super().__init__()
        
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class 'object'>)

d = D()
d

#↑ class D init
#  class C init
#  class B init

I tried playing with multiple inheritance

It's a little long from here. I am self-satisfied.

Setting

** Common to all races ** Attributes: name, life, ʻage, mana, stamina Methods: ʻattack, magic, defense, down

** Race-specific **

Human Elf
attribute luck(luck) suction_power(Suction power?)
Method 1 sword_skill bow_skill
Method 2 life_up drain

Define class

Inheriting the base class Race (race), we are creating the Human and ʻElf` classes.

import random

class Race(object):
    def __init__(self, name, age, life, mana, stamina):
        self.name = name
        self.age = age
        self.life = life
        self.mana = mana
        self.stamina = stamina
        
    def attack(self):
        self.stamina -= 10
        print(f'{self.name}Hit the monster!')
        
    def magic(self):
       self.mana -= 10
       print(f'{self.name}Frozen monsters!')
        
    def defence(self):
        self.life -= 1
        print(f'{self.name}Prevented the attack!')
        
    def down(self):
        self.life -= 10
        print(f'{self.name}Was attacked!')
    
    
class Human(Race):
    def __init__(self, name, age, life, mana, stamina, luck):
        super(Human, self).__init__(name, age, life, mana, stamina)
        self.luck = luck
    
    def sword_skill(self):
        self.stamina -= 20
        self.life += 10
        print(f'{self.name}Slaughtered a monster with a quick sword judgment!')
        print(f'{self.name}Stealed life from monsters!')
        
    #Life is restored with a random value
    def life_up(self):
        self.life_incremental = self.life + random.randint(1, 5) * self.luck
        self.life = self.life + self.life_incremental
        print(f'God on Our Side! ({self.name}Life{self.life_incremental}I've recovered! )')
        
    
class Elf(Race):
    def __init__(self, name, age, life, mana, stamina, suction_power):
        super(Elf, self).__init__(name, age, life, mana, stamina)
        self.suction_power = suction_power
    
    def bow_skill(self):
        self.stamina -= 20
        self.mana += 10
        print(f'{self.name}Slaughtered a monster from afar!')
        print(f'{self.name}Sucked mana from a monster!')
        
    def drain(self):
        self.mana_incremental = self.mana + random.randint(1, 5) * self.suction_power
        self.mana = self.mana + self.mana_incremental
        print(f'God on Our Side!{self.name}Mana{self.mana_incremental}I've recovered! )')

Play in the defined class

kirito = Human('Kirito', 18, 300, 200, 200, 10)
kirito.attack()
kirito.magic()
kirito.defence()
kirito.down()
kirito.sword_skill()
kirito.life_up()

print('---' * 10)

shinon = Elf('Sinon', 18, 200, 300, 200, 15)
shinon.attack()
shinon.magic()
shinon.defence()
shinon.down()
shinon.bow_skill()
shinon.drain()

result


Kirito hit the monster!
Frozen Kirito monsters!
Kirito prevented the attack!
Kirito was attacked!
Kirito slaughtered a monster with a quick sword judgment!
Kirito robbed the monster of life!
God on Our Side! (Kirito's life has recovered 319!)
------------------------------
Chinon hit the monster!
Frozen the Chinon monster!
Prevented the Chinon attack!
I got a Chinon attack!
Chinon slaughtered a monster from afar!
Chinon sucked mana from the monster!
God on Our Side! Chinon has recovered 375 mana! )

Birth of a half elf

Create a class HalfElf that inherits the class Human and the class ʻElf` multiple times. ** What I want to do is to create a race: half-elf that has all the attributes and methods specific to the race as well as the shared attributes and methods **

First, let's inherit Human and ʻElf without touching __init __`.

Do not touch the initializer


class HalfElf(Human, Elf):
    def super_drain(self):
        self.super_drain = (self.luck + self.suction_power) * random.randint(1, 5) 
        print(f'God on Our Side!{self.name}Mana from monsters{self.super_drain}I sucked it up!')

Examine MRO


print(HalfElf.__mro__)
# (<class '__main__.HalfElf'>, <class '__main__.Human'>, <class '__main__.Elf'>, <class '__main__.Race'>, <class 'object'>)

It seems that you are searching for Human first. In multiple inheritance, the classes called by super (own class, self) change in the order of MRO, so super () .__ init__ of the Human class becomes a parameter of __init__ of the ʻElfclass. Attempts to pass its own value. However, thesuction_power that exists in ʻElf is not in Human, and conversely, the luck that exists in Human is not in ʻElf`, so it just throws an error.

To solve it, you have to use super () in the above code and pass the appropriate class name as an argument to super (). More on that later.

Examine the attributes

What are the attributes of HalfElf?


asuna = HalfElf()
# TypeError: __init__() missing 6 required positional arguments: 'name', 'age', 'life', 'mana', 'stamina', and 'luck'

The attributes have name, ʻage, life, mana, stamina, luck. Does not have suction_power. This is because it calls Human's init`.

Examine the method

print(dir(HalfElf))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'attack', 'bow_skill', 'defence', 'down', 'drain', 'life_up', 'magic', 'super_drain', 'sword_skill']

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'attack', 'bow_skill', 'defence', 'down', 'drain', 'life_up', 'magic', 'super_drain', 'sword_skill'] It became .

Everything has been taken over.

Why is suction_power not taken over?

Since it does not describe the initializer, it is exactly the same as the Human class initializer that is searched first in MRO, so suction_power is not inherited.

Furthermore, for single inheritance, Human should use super to call the initializer of the base class Race, but this time it is multiple inheritance, so it calls ʻElfaccording to the root of MRO. .. However, inHuman, the attributesuction_power` does not exist. Therefore, it always throws an error at instance time.

error


class HalfElf(Human, Elf):
    def super_drain(self):
        self.super_drain = (self.luck + self.suction_power) * random.randint(5, 10) 
        print(f'God on Our Side!{self.name}Mana from monsters{self.super_drain}I sucked it up!')
             
        
        
asuna = HalfElf('Asuna', 18, 200, 300, 200, 10, suction_power=10)
# TypeError: __init__() got an unexpected keyword argument 'suction_power'
#Suction on Human properties_Because there is no power

asuna = HalfElf('Asuna', 18, 200, 300, 200, 10)
# TypeError: __init__() missing 1 required positional argument: 'suction_power'
#A property of the calling Elf class is suction_Because no value has been passed to power

suna = HalfElf('Asuna', 18, 200, 300, 200, 10, 10)
# TypeError: __init__() takes 7 positional arguments but 8 were given
#Because the Human class has 7 arguments in all

I think that it does not work well because the properties defined in the two parent classes are different and the method search route of multiple inheritance changes in the order of MRO. (difficult....!!)

To solve this, we did the following:

super(HalfElf, self).__init__() I called the initializer of the next class Human of my class. In this case, the human class initializer is called. However, when calling the ʻElf class with superin theHuman class, the property of super () .__ init__ (in this) does not have suction_power and does not match the property of ʻElf`, so an Error occurs. I will come out. (Maybe ... I don't understand clearly ...)

error


class HalfElf(Human, Elf):
    def __init__(self, name, age, life, mana, stamina, luck, suction_power):
        super(HalfElf, self).__init__(name, age, life, mana, stamina, luck)
        self.suction_power = suction_power
        
    def super_drain(self):
        self.super_drain = (self.luck + self.suction_power) * random.randint(5, 10) 
        print(f'God on Our Side!{self.name}Mana from monsters{self.super_drain}I sucked it up!')

asuna = HalfElf('Asuna', 18, 200, 300, 200, 10, 10)

'''
  File "Main.py", line 85, in <module>
    asuna = HalfElf('Asuna', 18, 200, 300, 200, 10, 10)
  File "Main.py", line 75, in __init__
    super(HalfElf, self).__init__(name, age, life, mana, stamina, luck)
  File "Main.py", line 30, in __init__
    super(Human, self).__init__(name, age, life, mana, stamina)
TypeError: __init__() missing 1 required positional argument: 'suction_power'
'''

super(Human, self).__init__() Called the initializer for the next class ʻElfof the Human class. In Elf,super calls the Race class, but since the properties of the ʻElf class satisfy the properties of the Race class, no error is placed. Instead, the Human class luck has not been taken over at all, so I will supplement it with self.luck = luck again.

Worked well


class HalfElf(Human, Elf):
    def __init__(self, name, age, life, mana, stamina, suction_power, luck):
        super(Human, self).__init__(name, age, life, mana, stamina, suction_power)
        self.luck = luck
        
    def super_drain(self):
        self.super_drain = (self.luck + self.suction_power) * random.randint(5, 10) 
        print(f'God on Our Side!{self.name}Mana from monsters{self.super_drain}I sucked it up!')

asuna = HalfElf('Asuna', 18, 200, 300, 200, 10, 10)
asuna.attack()
asuna.magic()
asuna.defence()
asuna.down()
asuna.sword_skill()
asuna.life_up()
asuna.bow_skill()
asuna.drain()
asuna.super_drain()

'''
Asuna hit the monster!
Frozen Asuna Monster!
Prevented Asuna attack!
I got an Asuna attack!
Asuna slaughtered a monster with a quick sword judgment!
Asuna robbed the monster of life!
God on Our Side! (Asuna's life has recovered 229!)
I slaughtered a monster from a distance!
I sucked mana from a monster!
God on Our Side! Asuna has recovered 330 mana! )
God on Our Side! Asuna sucked 200 mana from the monster!
'''

Try playing

I thought it would be easy to create a Half Elf by inheriting Human and ʻElf`, but that wasn't the case at all and it was very difficult. Rewriting the code of the base class to create multiple inheritance will affect other code, so I would like to learn more about how to write a class. Next time I want to try Mixin.

Recommended Posts

[Python] What is inherited by multiple inheritance?
What is python
What is Python
[Python] What is Pipeline ...
[Python] What is virtualenv
[Python] Python and security-① What is Python?
[Python] * args ** What is kwrgs?
What is a python map?
Python Basic Course (1 What is Python)
What is Python? What is it used for?
[Python] What is a zip function?
[Python] What is a with statement?
[Python] Sort iterable by multiple conditions
[Python] What is @? (About the decorator)
[python] What is the sorted key?
Python for statement ~ What is iterable ~
What is the python underscore (_) for?
Python> What is an extended slice?
Multiple inheritance
Explain what is stochastic gradient descent by running it in Python
Python #inheritance (inheritance)
[Python] What is pandas Series and DataFrame?
What is NaN? NaN Zoya (Python) (394 days late)
What kind of programming language is Python?
Python learning basics ~ What is type conversion? ~
What is "mahjong" in the Python library? ??
What is a dog? Python installation volume
Blender 2.9, Python, Select multiple meshes by coordinates
What is namespace
[What is an algorithm? Introduction to Search Algorithm] ~ Python ~
Why Python slicing is represented by a colon (:)
What is copy.copy ()
What is "functional programming" and "object-oriented" in Python?
Python is easy
What is Django? .. ..
What is dotenv?
What is POSIX?
What is wheezy in the Docker Python image?
What is Linux
What is klass?
I tried Python! ] I graduated today from "What is Python! Python!"!
What is SALOME?
About python inheritance
[Python] Ravel () is convenient when drawing multiple graphs
What is Linux?
What are you comparing with Python is and ==?
What is hyperopt?
Python is instance
What is Linux
[Introduction to Udemy Python 3 + Application] 54. What is Docstrings?
Change what is displayed by Dango authorized person
What is pyvenv
What is __call__
What is Linux
Tell me what a conformal map is, Python!
[Ruby / Python / Java / Swift / JS] What is an algorithm?
[Python] What is energy data? Calender Heatmap [Plotly] Memo
[Statistics] Understand what an ROC curve is by animation.
Python3 datetime is faster just by specifying the timezone
Basics of Python learning ~ What is a string literal? ~
What is God? Make a simple chatbot with python