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.
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 **.
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 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.
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? ..
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.
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'>).
__init__ when inheriting multiple timesWhen 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
It's a little long from here. I am self-satisfied.
** 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 |
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! )')
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! )
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!')
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.
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`.
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.
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!
'''
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