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 to
C` 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, the
suction_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, in
Human, the attribute
suction_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 the
Human 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