[Python] Qu'est-ce qui est hérité par l'héritage multiple?

À propos de cet article

Je veux remplacer Django et changer ses fonctionnalités, mais je ne sais pas comment le faire ... J'ai étudié parce que je ne pouvais pas lire le code multi-joint en premier lieu. Dans cet article, j'aimerais organiser le mécanisme et l'utilisation de l'héritage multiple de Python, et enfin jouer avec.

Qu'est-ce que l'héritage?

L'héritage est l'héritage de la fonctionnalité d'une classe.

#Classe de base
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'la satisfaction{self.satisfaction}Boire Hoffy du point')


coffee = Coffee('brown', 10, 200, 10, 300)
coffee.drink() # <---Buvez Hoffy avec 3700 points de satisfaction

#Hériter (classe dérivée)
class Coffee2(Coffee): 
    pass # <-A exactement la même fonction que le café

class Nihonsyu(Coffee2):
    pass # <-A exactement la même fonction que Coffee2

Les classes héritées peuvent ajouter des fonctionnalités en ajoutant ou en remplaçant des méthodes. L'héritage d'une classe comme class Coffee2 (Coffee): est appelé ** héritage unique **.

Ordre d'héritage

Dans le cas de l'héritage unique comme décrit ci-dessus, vous pouvez le découvrir en traçant simplement la source d'héritage. Nihonsyu <- Coffee2 <- Coffee

L'héritage de classe recherche les méthodes dans l'ordre d'héritage et hérite de ces méthodes. À propos, lorsque plusieurs classes parentes ont le même nom de méthode, seule la méthode de la première classe parente héritée peut être héritée. Cependant, si super () est défini dans la méthode de la classe parente, toutes les fonctions de la classe parente et de la classe parente peuvent être héritées. Dans tous les cas, si vous souhaitez remplacer plusieurs méthodes d'héritage, vous devez comprendre quelles méthodes sont héritées. Pour ce faire, il semble nécessaire de connaître l'ordre d'héritage de la classe parente.

MRO vous le dira

MRO est appelé «Ordre de résolution des méthodes». Il s'agit de l'ordre dans lequel les méthodes sont recherchées lors de l'héritage d'une classe. La classe de base de Python ʻobject` a une méthode appelée mro, qui peut être utilisée pour connaître le MRO, alors essayons-la avec le code ci-dessus.


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

Il était affiché sous la forme (<class '__ main__. Nihonsyu'>, <class '__ main__. Coffee2'>, <class '__ main__. Coffee'>, <class'object '>). Comme vous pouvez le voir, j'ai pu afficher l'ordre de recherche des méthodes.

Qu'est-ce que l'héritage multiple?

L'héritage multiple est l'état lorsque deux classes ou plus sont passées comme argument de la classe.

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): # <-Héritage multiple
    pass

D est une classe multi-héritée, que sera généré lors de l'exécution de la méthode hello sur une instance de D?

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

Bonjour de C est sorti. En d'autres termes, il hérite de la méthode de classe C. Puisqu'il s'agit de la «classe D (B, C)», je pensais que la «classe B» serait prioritaire et que le «Bonjour de A» de la classe A, qui hérite de toute la classe B, serait affiché. Pourquoi? ..

Ordre d'héritage multiple

Il semble qu'il existe des algorithmes de «priorité de profondeur» et de «priorité de largeur» qui déterminent l'ordre d'héritage multiple, mais Python semble adopter un algorithme qui n'est ni de type «C linéaire». Je vais omettre cela car ce sera une histoire profonde, mais il semble que le MRO change en fonction de la structure de l'héritage multiple, et la recherche est moins redondante. Pour le moment, vous pouvez trouver le MRO en utilisant __mro__, alors vérifions-le.

MRO vous le dira

Essayez-le avec le code d'héritage multiple ci-dessus.


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

Il est devenu (<class '__ main __. D'>, <class '__ main__. B'>, <class '__ main__. C'>, <class '__ main__. A'>, <class'object '>).

Précautions pour «init» lors de l'héritage de plusieurs fois

Lorsque l'héritage multiple est effectué, les méthodes, etc. sont héritées selon MRO, mais il semble que les méthodes portant le même nom ne peuvent hériter que de celles de la première classe héritée. Ce serait bien si une méthode avec le même nom n'existe pas, mais comme __init__ existe presque toujours, il semble qu'il soit nécessaire d'appeler fermement init de la classe dont vous voulez hériter avec super () et d'effectuer correctement le processus d'initialisation. ..

De plus, le «init» dans le code «classe C» ci-dessous semble remplacer le «init» de la «classe d'objet», mais au moment de l'héritage multiple, les méthodes suivent l'ordre de «MRO». Il semble remplacer «init» de «B» à côté de «C» car il semble écraser.

** N'oubliez pas que l'ordre des méthodes remplacées par super () change pendant l'héritage multiple **


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

J'ai essayé de jouer avec l'héritage multiple

C'est un peu long d'ici. Je suis satisfait de moi-même.

Réglage

** Commun à toutes les races ** Attributs: nom, vie, ʻage, mana, endurance` Méthodes: «attaque», «magie», «défense», «bas»

** Spécifique à la course **

Humain Elfe
attribut luck(la chance) suction_power(Puissance d'aspiration?)
Méthode 1 sword_skill bow_skill
Méthode 2 life_up drain

Définir la classe

En héritant de la classe de base Race (race), nous créons les classes Human et ʻElf`.

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}Frappez le monstre!')
        
    def magic(self):
       self.mana -= 10
       print(f'{self.name}Monstres gelés!')
        
    def defence(self):
        self.life -= 1
        print(f'{self.name}Empêché l'attaque!')
        
    def down(self):
        self.life -= 10
        print(f'{self.name}A été attaqué!')
    
    
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}Vous avez massacré un monstre avec un savoir-faire rapide!')
        print(f'{self.name}Volé la vie des monstres!')
        
    #La vie est restaurée avec une valeur aléatoire
    def life_up(self):
        self.life_incremental = self.life + random.randint(1, 5) * self.luck
        self.life = self.life + self.life_incremental
        print(f'Dieu a pris le parti! ({self.name}La vie{self.life_incremental}J'ai récupéré! )')
        
    
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}Abattu un monstre de loin!')
        print(f'{self.name}Mana sucé par un monstre!')
        
    def drain(self):
        self.mana_incremental = self.mana + random.randint(1, 5) * self.suction_power
        self.mana = self.mana + self.mana_incremental
        print(f'Dieu a pris le parti!{self.name}Mana{self.mana_incremental}J'ai récupéré! )')

Jouez dans la classe définie

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()

résultat


Kirito a frappé le monstre!
Monstre congelé de Kirito!
Kirito a empêché l'attaque!
Kirito a été attaqué!
Kirito a massacré un monstre avec un savoir-faire rapide!
Kirito a volé le monstre de la vie!
Dieu a pris le parti! (La vie de Kirito a récupéré 319!)
------------------------------
Shinon a frappé le monstre!
Monstre Shinon gelé!
Attaque de Shinon empêchée!
J'ai eu une attaque de Shinon!
Shinon a massacré un monstre de loin!
Shinon a sucé le mana du monstre!
Dieu a pris le parti! Shinon a récupéré 375 points de mana! )

Naissance d'un demi elfe

Créez une classe HalfElf qui hérite plusieurs fois de la classe Human et de la classe ʻElf`. ** Ce que je veux faire, c'est créer une race: demi elfe qui possède tous les attributs et méthodes spécifiques à la race ainsi que les attributs et méthodes partagés **

D'abord, héritons de Human et de ʻElf sans toucher __init __`.

Ne touchez pas l'initialiseur


class HalfElf(Human, Elf):
    def super_drain(self):
        self.super_drain = (self.luck + self.suction_power) * random.randint(1, 5) 
        print(f'Dieu a pris le parti!{self.name}Mana des monstres{self.super_drain}Je l'ai sucé!')

Examiner MRO


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

Il semble que vous cherchiez d'abord «Humain». Dans l'héritage multiple, les classes appelées par super (propre classe, self) changent dans l'ordre de MRO, donc super () .__ init__ de la classe Human devient un paramètre de __init__ de la classe ʻElf. Tente de transmettre sa propre valeur. Cependant, la puissance_aspiration qui existe dans ʻElf n'est pas dans Humain, et inversement, la chance qui existe dans Humain n'est pas dans ʻElf`, donc elle lance juste une erreur.

Pour le résoudre, vous devez utiliser super () dans le code ci-dessus et passer le nom de classe approprié à l'argument de super (). Plus à ce sujet plus tard.

Examiner les attributs

Quels sont les attributs de «HalfElf»?


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

Les attributs ont «name», «age», «life», «mana», «stamina», «luck». N'a pas aspiration_power. Parce qu'il appelle Human 'init`.

Examiner la méthode

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'] C'est devenu ".

Tout a été repris.

Pourquoi aspiration_power n'est pas pris en charge?

Puisqu'il ne décrit pas l'initialiseur, il sera exactement le même que l'initialiseur de classe Human qui est recherché en premier dans MRO, donc aspiration_power ne sera pas hérité.

De plus, pour l'héritage unique, Human devrait utiliser super pour appeler l'initialiseur de la classe de base Race, mais cette fois c'est un héritage multiple, donc il appelle ʻElf` selon la racine de MRO. .. Cependant, l'attribut «puissance_aspiration» n'existe pas dans «Humain». Par conséquent, il génère toujours une erreur au moment de l'instance.

error


class HalfElf(Human, Elf):
    def super_drain(self):
        self.super_drain = (self.luck + self.suction_power) * random.randint(5, 10) 
        print(f'Dieu a pris le parti!{self.name}Mana des monstres{self.super_drain}Je l'ai sucé!')
             
        
        
asuna = HalfElf('Asuna', 18, 200, 300, 200, 10, suction_power=10)
# TypeError: __init__() got an unexpected keyword argument 'suction_power'
#Aspiration sur les propriétés humaines_Parce qu'il n'y a pas de pouvoir

asuna = HalfElf('Asuna', 18, 200, 300, 200, 10)
# TypeError: __init__() missing 1 required positional argument: 'suction_power'
#Il y a une propriété de la classe Elf que vous appelez_Parce qu'aucune valeur n'a été passée au pouvoir

suna = HalfElf('Asuna', 18, 200, 300, 200, 10, 10)
# TypeError: __init__() takes 7 positional arguments but 8 were given
#Parce que la classe Humaine a 7 arguments en tout

Je pense que cela ne fonctionne pas bien car les propriétés définies dans les deux classes parentes sont différentes et la route de recherche de méthode de plusieurs héritages change dans l'ordre de MRO. (difficile....!!)

Pour résoudre ce problème, nous avons procédé comme suit:

super(HalfElf, self).__init__() J'ai appelé l'initialiseur de la classe suivante Human de ma classe. Dans ce cas, l'initialiseur de classe Human est appelé. Cependant, lors de l'appel de la classe ʻElf avec superdans la classeHuman, la propriété de super () .__ init__ (dans ce cas) n'a pas de puissance_spiration et ne correspond pas à la propriété de ʻElf`, donc une erreur se produit. Je vais sortir. (Peut-être ... je ne comprends pas clairement ...)

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'Dieu a pris le parti!{self.name}Mana des monstres{self.super_drain}Je l'ai sucé!')

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__() Appelé l'initialiseur de la classe suivante ʻElfde la classe Human. Dans Elf,super appelle la classe Race, mais comme les propriétés de la classe ʻElf satisfont les propriétés de la classe Race, aucune erreur n'est placée. Au lieu de cela, la chance de la classe Human n'a pas du tout été prise en charge, donc je la compléterai à nouveau par self.luck = luck.

A bien fonctionné


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'Dieu a pris le parti!{self.name}Mana des monstres{self.super_drain}Je l'ai sucé!')

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 a frappé le monstre!
Monstre Asuna gelé!
Attaque Asuna empêchée!
J'ai eu une attaque Asuna!
Asuna a abattu un monstre avec un savoir-faire rapide!
Asuna a volé le monstre de la vie!
Dieu a pris le parti! (La vie d'Asuna a récupéré 229!)
J'ai massacré un monstre à distance!
J'ai sucé le mana d'un monstre!
Dieu a pris le parti! Asuna a récupéré 330 points de mana! )
Dieu a pris le parti! Asuna a sucé 200 mana du monstre!
'''

Essayez de jouer

Je pensais que si j'héritais de «Human» et «Elf», je pourrais facilement créer «HalfElf», mais ce n'était pas du tout le cas et c'était très difficile. La réécriture du code de la classe de base pour créer un héritage multiple affectera un autre code, je voudrais donc en savoir plus sur la façon d'écrire une classe. Ensuite, je veux essayer Mixin.

Recommended Posts

[Python] Qu'est-ce qui est hérité par l'héritage multiple?
Qu'est-ce que python
Qu'est-ce que Python
[Python] Qu'est-ce que Pipeline ...
[Python] Qu'est-ce que virtualenv
[Python] Python et sécurité-① Qu'est-ce que Python?
[Python] * args ** Qu'est-ce que kwrgs?
Cours de base Python (1 Qu'est-ce que Python)
[Python] Qu'est-ce qu'une fonction zip?
[Python] Qu'est-ce qu'une instruction with?
[Python] Tri itérable selon plusieurs conditions
[Python] Qu'est-ce que @? (À propos des décorateurs)
[python] Quelle est la clé triée?
Python pour la déclaration ~ Qu'est-ce qui est itérable ~
À quoi sert le trait de soulignement Python (_)?
Python> Qu'est-ce qu'une tranche étendue?
Héritage multiple
Expliquez ce qu'est la méthode de descente de gradient stochastique en l'exécutant en Python
Python #inheritance (héritage)
[Python] Qu'est-ce que la série pandas et DataFrame?
Qu'est-ce que NaN? NaN Zoya (Python) (394 jours de retard)
Quel type de langage de programmation est Python?
Qu'est-ce que "mahjong" dans la bibliothèque Python? ??
Qu'est-ce qu'un chien? Volume d'installation Python
Blender 2.9, Python, sélectionnez plusieurs maillages par coordonnées
Qu'est-ce que l'espace de noms
Qu'est-ce qu'un algorithme? Introduction à l'algorithme de recherche] ~ Python ~
Pourquoi le découpage Python est représenté par un deux-points (:)
Qu'est-ce que copy.copy ()
Qu'est-ce que la «programmation fonctionnelle» et «orientée objet»? Édition Python
Python est facile
Qu'est-ce que Django? .. ..
Qu'est-ce que dotenv?
Qu'est-ce que POSIX
Qu'est-ce que wheezy dans l'image Docker Python?
Qu'est-ce que Linux
Qu'est-ce que le klass?
J'ai essayé Python! ] Diplômé aujourd'hui de "Tout le monde Python! Qu'est-ce que Python!"!
Qu'est-ce que SALOME?
À propos de l'héritage Python
[Python] Ravel () est pratique pour dessiner plusieurs graphiques
Qu'est-ce que Linux?
Que comparez-vous avec Python et ==?
Qu'est-ce que l'hyperopt?
Python est une instance
Qu'est-ce que Linux
[Introduction à l'application Udemy Python3 +] 54. Qu'est-ce que Docstrings?
Changer ce qui est affiché par la personne autorisée Dango
Qu'est-ce que pyvenv
Qu'est-ce que __call__
Qu'est-ce que Linux
Dites-moi ce qu'est une cartographie équiangulaire, Python!
Qu'est-ce que l'algorithme [Ruby / Python / Java / Swift / JS]?
[Python] Calender Heatmap [Plotly] Memo
[Statistiques] Comprenez ce qu'est la courbe ROC par animation.
Le datetime Python3 est plus rapide en spécifiant simplement le fuseau horaire
Qu'est-ce que Dieu? Créez un chatbot simple avec python