Python >> Tutoriel Python >  >> Python

Héritage en Python

Tu as les yeux de ta mère. On pourrait dire que vous avez « hérité » des yeux de votre mère. Comme vous l'avez peut-être deviné, cet article traite de l'héritage en Python.

L'héritage est l'une des caractéristiques les plus importantes de l'orientation objet. C'est un concept simple et intuitif mais même les codeurs avancés contournent l'héritage car ils ont ce sentiment latent de ne pas le comprendre à 100%. Cela nuit à leur capacité à écrire du code propre et efficace et, en fin de compte, à leur capacité à exploiter pleinement leur potentiel. Pensez-vous que vous pourriez faire plus pour être à la hauteur de votre potentiel ? Bien. Ensuite, commençons à apprendre l'héritage en Python.

En guise de petit cadeau d'appréciation pour la lecture de ce didacticiel et l'amélioration de vos compétences en programmation, vous obtiendrez un téléchargement PDF gratuit de ma feuille de triche sur l'orientation des objets à la fin de ce didacticiel.

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

L'héritage vous permet de définir une classe qui hérite de toutes les méthodes et propriétés d'une autre classe.

  • Classe parent , également désignée par classe de base , est la classe dont vous héritez. En Python, chaque classe peut être une classe parent.
  • Classe enfant , également désignée par classe dérivée , hérite de la classe Parent. En Python, vous pouvez créer une classe enfant qui hérite de toutes les méthodes et attributs du parent en utilisant le class Child(Parent) syntaxe avec la classe parent entre parenthèses.

Voici la syntaxe minimale montrant comment dériver une classe enfant d'une classe parent à l'aide de la méthode des parenthèses en surbrillance :

class Parent:
    pass

class Child(Parent):
    pass

Dans l'exemple suivant, vous créez deux classes Parent et Child . Parent a une méthode p() qui imprime la chaîne 'from parent' lorsqu'il est exécuté. Child a la méthode c() qui imprime la chaîne 'from child' et la méthode héritée p() du Parent classer.

# Define parent and child classes
class Parent:
    def p(self):
        return 'from parent'


# Child inherits method p() from parent
class Child(Parent):
    def c(self):
        return 'from child'


# Create parent instance and run method
parent = Parent()
print(parent.p())

# Create child instance and run methods
child = Child()
print(child.p())
print(child.c())

La sortie est :

from parent
from parent
from child

Vous pouvez voir que l'enfant hérite de toutes les méthodes et, non affiché, de tous les attributs de la classe parent. Cela peut vous éviter de nombreuses redondances dans la pratique.

Vue d'ensemble de la vidéo

Puzzle et exemple négatif

Jetons un coup d'œil à un mauvais exemple N'UTILISANT PAS l'héritage. Gardez vos compétences en compréhension du code à jour en résolvant le puzzle avant de poursuivre votre lecture.

class Human:

    def __init__(self, name, ff, iq):
        self.name = name
        self.ff = ff # = facebook friends
        self.iq = iq

    def befriend(self, other):
        self.ff += 1
        other.ff += 1

    def learn(self):
        self.iq += 1



class Wizard:

    def __init__(self, name, ff, iq, mana):
        self.name = name
        self.ff = ff # = facebook friends
        self.iq = iq
        self.mana = mana

    def befriend(self, other):
        self.ff += 1
        other.ff += 1

    def learn(self):
        self.iq += 1
    
    def magic_friends(self, num):
        self.ff += num if self.mana>0 else 0
        self.mana -= 100


vernon = Human("Vernon", 0, 80)
tom = Wizard("Tom", 666, 130, 100)
dumbledore = Wizard("Albus", 999, 189, 100)

dumbledore.befriend(tom)
dumbledore.befriend(vernon)
dumbledore.magic_friends(100)

print("Friends Vernon: " + str(vernon.ff))
print("Friends Tom: " + str(tom.ff))
print("Friends Dumbledore: " + str(dumbledore.ff))

Avant de poursuivre votre lecture, j'ai deux questions à vous poser :

  1. Quel est le résultat de l'extrait de code ci-dessus ?
  2. Quelle est votre idée de rendre ce code plus concis ?

Quel est le résultat de l'extrait de code ci-dessus ?

Commençons par la première question. Nous créons deux classes Human et Wizards . Les deux ont des méthodes et des attributs très similaires. La seule différence est que le Wizard la classe a un attribut supplémentaire self.mana et une méthode supplémentaire magic_friends . Les deux méthodes befriend et magic_friends modifier l'attribut d'instance ff . Avec un tour de magie, Dumbledore gagne 100 amis supplémentaires, en plus de Tom et Vernon.

Ainsi, le résultat est :

"""
Friends Vernon: 1
Friends Tom: 667
Friends Dumbledore: 1101
"""

Comment améliorer ce code ?

Concernant la deuxième question, j'ai déjà indiqué le problème :il y a d'énormes redondances entre les deux classes Human et Wizard . La plupart des méthodes et des attributs sont exactement les mêmes. La raison est que, conceptuellement, un Wizard est aussi Human . Et chaque être humain doit avoir un QI, un compte Facebook et un nom (comme vous le savez).

En d'autres termes :tous les Wizard est un Human mais pas tous les Human est un Wizard .

Comment pouvons-nous exprimer ce fait dans l'orientation objet de Python ?

La réponse est l'héritage.

Nous créons une classe Human et une classe Wizard . La classe Wizard est la "classe enfant" de la "classe parent" Human . De cette manière, la classe enfant Wizard "hérite" de chaque attribut et méthode de la classe parente Human . Cela nous évite toutes les définitions et initialisations redondantes dans le Wizard classer.

Voir l'exemple :

class Human:


    def __init__(self, name, ff, iq):
        self.name = name
        self.ff = ff # = facebook friends
        self.iq = iq

    def befriend(self, other):
        self.ff += 1
        other.ff += 1

    def learn(self):
        self.iq += 1



class Wizard(Human):


    def __init__(self, name, ff, iq, mana):
        super().__init__(name, ff, iq)
        self.mana = mana

    def magic_friends(self, num):
        self.ff += num if self.mana>0 else 0
        self.mana -= 100


vernon = Human("Vernon", 0, 80)
tom = Wizard("Tom", 666, 130, 100)
dumbledore = Wizard("Albus", 999, 189, 100)

dumbledore.befriend(tom)
dumbledore.befriend(vernon)
dumbledore.magic_friends(100)

print("Friends Vernon: " + str(vernon.ff))
print("Friends Tom: " + str(tom.ff))
print("Friends Dumbledore: " + str(dumbledore.ff))

dumbledore.learn()
print("IQ Dumbledore: " + str(dumbledore.iq))

Le résultat est exactement le même que ci-dessus. Comme vous pouvez le voir dans les deux dernières lignes, Dumbledore peut toujours appeler la méthode learn() — même s'il n'est pas défini dans le Wizard classer. La raison est que le Wizard la classe hérite de toutes les méthodes et attributs du Human classer.

Pouvez-vous trouver où nous définissons l'héritage dans le code ?

Une dernière note :dans le constructeur du Wizard class, nous appelons le constructeur de la classe parent en utilisant "super()" . Cela initialise les variables exactement de la même manière que le constructeur parent du Human classe.

Appeler le constructeur de la classe parent avec super()

super() intégré de Python La méthode renvoie un objet temporaire de la superclasse pour vous aider à accéder à ses méthodes. Son but est d'éviter d'utiliser explicitement le nom de la classe de base. Cela permet également à votre classe d'hériter de plusieurs classes de base.

L'idée est simple :utilisez super() pour appeler les méthodes définies dans les classes parentes, que votre classe enfant hérite d'une ou de plusieurs classes de base. Voir le graphique :

Besoin de moi pour vous guider à travers cet exemple? Regardez ensuite la vidéo explicative !

Ensuite, vous découvrirez les deux cas par exemple !

Exemple simple 1 :super() avec héritage unique

Héritage dans la programmation orientée objet vous permet de créer une hiérarchie de classes dans laquelle une classe enfant hérite de toutes les méthodes d'une autre classe parent. Cela simplifie le développement de grands projets logiciels et évite le code redondant. Vous pouvez en savoir plus sur le concept d'héritage dans notre tutoriel de blog ici.

Par exemple, le code suivant définit la classe parente Organism et la classe enfant Human . La classe enfant utilise super() pour exécuter la méthode constructeur de la classe parent.

class Organism:
    def __init__(self):
        print('I live')


class Human(Organism):
    def __init__(self):
        print('I am human')
        super().__init__()


alice = Human()

La sortie est :

I am human
I live

Ici, vous appelez la classe de base Organism en utilisant le code suivant :

super().__init__()

Un appel de code sémantiquement équivalent serait :

Organism.__init__(self)

Vous appelez le __init__() méthode sur la classe de base Organism et passez une référence à l'instance appelante en tant qu'argument. De cette façon, vous pouvez également modifier les attributs internes du self instance dans le constructeur de la classe de base.

Cependant, l'avantage d'utiliser super().__init__() par rapport à ParentClass.__init__(self) est que vous évitez d'appeler explicitement la classe parent. Ceci est avantageux car cela dissocie l'enfant de la classe parent. Par exemple, si vous avez changé le nom du ParentClass à NewParentClass , la méthode utilisant super() serait supérieur car cela fonctionnerait toujours pendant que la méthode utilisant ParentClass.__init__(self) renverrait une erreur.

Exemple 2 :super() avec héritage multiple

L'une des caractéristiques uniques de Python par rapport aux autres langages de programmation est qu'il permet l'héritage multiple .

L'héritage multiple signifie qu'une classe peut hériter de plusieurs parents. Par exemple, une classe Human peut hériter de deux classes parentes :Organism et Thinker . Supposons que vous définissiez une méthode live() dans Organisme et think() dans Penseur. Si un objet humain hérite des deux classes, il peut appeler live() et think() à la fois! Vous utilisez le super() méthode pour appeler ces fonctions :

class Organism:
    def live(self):
        print('I live')


class Thinker:
    def think(self):
        print('I think')


class Human(Organism, Thinker):
    def __init__(self):
        print('I am human')
        super().live()
        super().think()
 

alice = Human()

La sortie est :

I am human
I live
I think

Je dois mentionner que dans cet exemple, vous auriez également pu appeler self.live() et self.think() dans la classe Human au lieu de super().live() et super().think() . La sortie serait la même dans les deux cas. En pratique, vous utiliseriez la première pour les méthodes d'instance et ce dernier pour les méthodes de classe . La différence entre les deux est expliquée dans notre tutoriel de blog ici.

Aide-mémoire Python POO

Félicitations, vous avez lu tout l'article. Voici une petite récompense pour vos efforts :mon aide-mémoire sur la terminologie orientée objet !

Téléchargez cette feuille de triche au format PDF

Vous pouvez également consulter mon didacticiel détaillé sur les feuilles de triche Python avec de nombreuses feuilles de triche PDF gratuites sur divers sujets en Python tels que les mots clés, les structures de données, les listes, les ensembles, NumPy, les pandas et bien d'autres. Mettez simplement votre e-mail ici et téléchargez mes feuilles de triche gratuites :