Introduction
Dans ce laboratoire, vous allez apprendre le comportement de l'héritage en Python. Plus précisément, vous vous concentrerez sur le fonctionnement de la fonction super() et sur la mise en œuvre de l'héritage coopératif. L'héritage est un concept fondamental en programmation orientée objet, permettant aux classes d'hériter d'attributs et de méthodes des classes parentes pour une réutilisation du code et la création de structures de classes hiérarchiques.
Dans cette expérience pratique, vous comprendrez les différents types d'héritage en Python, y compris l'héritage simple et multiple. Vous apprendrez également à utiliser la fonction super() pour naviguer dans les hiérarchies d'héritage, à implémenter un exemple pratique d'héritage multiple coopératif et à appliquer ces concepts pour construire un système de validation. Le fichier principal créé lors de ce laboratoire est validate.py.
Comprendre l'héritage simple et multiple
Dans cette étape, nous allons apprendre les deux principaux types d'héritage en Python : l'héritage simple et l'héritage multiple. L'héritage est un concept fondamental en programmation orientée objet qui permet à une classe d'hériter d'attributs et de méthodes d'autres classes. Nous allons également examiner comment Python détermine quelle méthode appeler lorsqu'il y a plusieurs candidats, un processus connu sous le nom de résolution de méthode.
Héritage simple
L'héritage simple se produit lorsque les classes forment une seule lignée d'ascendance. Imaginez - le comme un arbre généalogique où chaque classe n'a qu'un seul parent direct. Créons un exemple pour comprendre son fonctionnement.
Tout d'abord, ouvrez un nouveau terminal dans le WebIDE. Une fois le terminal ouvert, lancez l'interpréteur Python en tapant la commande suivante puis en appuyant sur Entrée :
python3
Maintenant que vous êtes dans l'interpréteur Python, nous allons créer trois classes qui forment une chaîne d'héritage simple. Entrez le code suivant :
class A:
def spam(self):
print('A.spam')
class B(A):
def spam(self):
print('B.spam')
super().spam()
class C(B):
def spam(self):
print('C.spam')
super().spam()
Dans ce code, la classe B hérite de la classe A, et la classe C hérite de la classe B. La fonction super() est utilisée pour appeler la méthode de la classe parente.
Après avoir défini ces classes, nous pouvons découvrir l'ordre dans lequel Python recherche les méthodes. Cet ordre est appelé l'ordre de résolution de méthode (Method Resolution Order - MRO). Pour voir le MRO de la classe C, tapez le code suivant :
C.__mro__
Vous devriez voir une sortie similaire à celle - ci :
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
Cette sortie montre que Python cherche d'abord une méthode dans la classe C, puis dans la classe B, ensuite dans la classe A, et enfin dans la classe de base object.
Maintenant, créons une instance de la classe C et appelons sa méthode spam(). Tapez le code suivant :
c = C()
c.spam()
Vous devriez voir la sortie suivante :
C.spam
B.spam
A.spam
Cette sortie démontre le fonctionnement de super() dans une chaîne d'héritage simple. Lorsque C.spam() appelle super().spam(), il appelle B.spam(). Ensuite, lorsque B.spam() appelle super().spam(), il appelle A.spam().
Héritage multiple
L'héritage multiple permet à une classe d'hériter de plusieurs classes parentes. Cela donne à une classe accès aux attributs et méthodes de toutes ses classes parentes. Voyons comment la résolution de méthode fonctionne dans ce cas.
Entrez le code suivant dans votre interpréteur Python :
class Base:
def spam(self):
print('Base.spam')
class X(Base):
def spam(self):
print('X.spam')
super().spam()
class Y(Base):
def spam(self):
print('Y.spam')
super().spam()
class Z(Base):
def spam(self):
print('Z.spam')
super().spam()
Maintenant, nous allons créer une classe M qui hérite de plusieurs classes parentes X, Y et Z. Entrez le code suivant :
class M(X, Y, Z):
pass
M.__mro__
Vous devriez voir la sortie suivante :
(<class '__main__.M'>, <class '__main__.X'>, <class '__main__.Y'>, <class '__main__.Z'>, <class '__main__.Base'>, <class 'object'>)
Cette sortie montre l'ordre de résolution de méthode pour la classe M. Python recherchera les méthodes dans cet ordre.
Créons une instance de la classe M et appelons sa méthode spam() :
m = M()
m.spam()
Vous devriez voir la sortie suivante :
X.spam
Y.spam
Z.spam
Base.spam
Notez que super() ne fait pas simplement appel à la méthode de la classe parente immédiate. Au lieu de cela, il suit l'ordre de résolution de méthode (MRO) défini par la classe enfant.
Créons une autre classe N avec les classes parentes dans un ordre différent :
class N(Z, Y, X):
pass
N.__mro__
Vous devriez voir la sortie suivante :
(<class '__main__.N'>, <class '__main__.Z'>, <class '__main__.Y'>, <class '__main__.X'>, <class '__main__.Base'>, <class 'object'>)
Maintenant, créez une instance de la classe N et appelez sa méthode spam() :
n = N()
n.spam()
Vous devriez voir la sortie suivante :
Z.spam
Y.spam
X.spam
Base.spam
Cela montre un concept important : dans l'héritage multiple de Python, l'ordre des classes parentes dans la définition de la classe détermine l'ordre de résolution de méthode. La fonction super() suit cet ordre, peu importe depuis quelle classe elle est appelée.
Lorsque vous avez terminé d'explorer ces concepts, vous pouvez quitter l'interpréteur Python en tapant le code suivant :
exit()
Construction d'un système de validation avec l'héritage
Dans cette étape, nous allons construire un système de validation pratique en utilisant l'héritage. L'héritage est un concept puissant en programmation qui vous permet de créer de nouvelles classes basées sur des classes existantes. De cette manière, vous pouvez réutiliser le code et créer des programmes plus organisés et modulaires. En construisant ce système de validation, vous verrez comment l'héritage peut être utilisé pour créer des composants de code réutilisables qui peuvent être combinés de différentes manières.
Création de la classe de base des validateurs
Tout d'abord, nous devons créer une classe de base pour nos validateurs. Pour ce faire, nous allons créer un nouveau fichier dans le WebIDE. Voici comment vous pouvez le faire : cliquez sur "File" > "New File", ou vous pouvez utiliser le raccourci clavier. Une fois le nouveau fichier ouvert, nommez - le validate.py.
Maintenant, ajoutons du code à ce fichier pour créer une classe de base Validator. Cette classe servira de fondation pour tous nos autres validateurs.
## validate.py
class Validator:
@classmethod
def check(cls, value):
return value
Dans ce code, nous avons défini une classe Validator avec une méthode check. La méthode check prend une valeur en argument et la renvoie simplement inchangée. Le décorateur @classmethod est utilisé pour transformer cette méthode en une méthode de classe. Cela signifie que nous pouvons appeler cette méthode sur la classe elle - même, sans avoir à créer une instance de la classe.
Ajout de validateurs de type
Ensuite, nous allons ajouter des validateurs qui vérifient le type d'une valeur. Ces validateurs hériteront de la classe Validator que nous venons de créer. Revenez au fichier validate.py et ajoutez le code suivant :
class Typed(Validator):
expected_type = object
@classmethod
def check(cls, value):
if not isinstance(value, cls.expected_type):
raise TypeError(f'Expected {cls.expected_type}')
return super().check(value)
class Integer(Typed):
expected_type = int
class Float(Typed):
expected_type = float
class String(Typed):
expected_type = str
La classe Typed est une sous - classe de Validator. Elle a un attribut expected_type, qui est initialement défini sur object. La méthode check dans la classe Typed vérifie si la valeur donnée est du type attendu. Si ce n'est pas le cas, elle lève une TypeError. Si le type est correct, elle appelle la méthode check de la classe parente en utilisant super().check(value).
Les classes Integer, Float et String héritent de Typed et spécifient le type exact qu'elles sont censées vérifier. Par exemple, la classe Integer vérifie si une valeur est un entier.
Test des validateurs de type
Maintenant que nous avons créé nos validateurs de type, testons - les. Ouvrez un nouveau terminal et lancez l'interpréteur Python en exécutant la commande suivante :
python3
Une fois que l'interpréteur Python est en cours d'exécution, nous pouvons importer et tester nos validateurs. Voici du code pour les tester :
from validate import Integer, String
Integer.check(10) ## Should return 10
try:
Integer.check('10') ## Should raise TypeError
except TypeError as e:
print(f"Error: {e}")
String.check('10') ## Should return '10'
Lorsque vous exécutez ce code, vous devriez voir quelque chose comme ceci :
10
Error: Expected <class 'int'>
'10'
Nous pouvons également utiliser ces validateurs dans une fonction. Essayons cela :
def add(x, y):
Integer.check(x)
Integer.check(y)
return x + y
add(2, 2) ## Should return 4
try:
add('2', '3') ## Should raise TypeError
except TypeError as e:
print(f"Error: {e}")
Lorsque vous exécutez ce code, vous devriez voir :
4
Error: Expected <class 'int'>
Ajout de validateurs de valeur
Jusqu'à présent, nous avons créé des validateurs qui vérifient le type d'une valeur. Maintenant, ajoutons des validateurs qui vérifient la valeur elle - même plutôt que le type. Revenez au fichier validate.py et ajoutez le code suivant :
class Positive(Validator):
@classmethod
def check(cls, value):
if value < 0:
raise ValueError('Expected >= 0')
return super().check(value)
class NonEmpty(Validator):
@classmethod
def check(cls, value):
if len(value) == 0:
raise ValueError('Must be non-empty')
return super().check(value)
Le validateur Positive vérifie si une valeur est non négative. Si la valeur est inférieure à 0, il lève une ValueError. Le validateur NonEmpty vérifie si une valeur a une longueur non nulle. Si la longueur est 0, il lève une ValueError.
Composition de validateurs avec l'héritage multiple
Maintenant, nous allons combiner nos validateurs en utilisant l'héritage multiple. L'héritage multiple permet à une classe d'hériter de plusieurs classes parentes. Revenez au fichier validate.py et ajoutez le code suivant :
class PositiveInteger(Integer, Positive):
pass
class PositiveFloat(Float, Positive):
pass
class NonEmptyString(String, NonEmpty):
pass
Ces nouvelles classes combinent la vérification de type et la vérification de valeur. Par exemple, la classe PositiveInteger vérifie qu'une valeur est à la fois un entier et non négative. L'ordre d'héritage est important ici. Les validateurs sont vérifiés dans l'ordre spécifié dans la définition de la classe.
Test des validateurs composés
Testons nos validateurs composés. Dans l'interpréteur Python, exécutez le code suivant :
from validate import PositiveInteger, PositiveFloat, NonEmptyString
PositiveInteger.check(10) ## Should return 10
try:
PositiveInteger.check('10') ## Should raise TypeError
except TypeError as e:
print(f"Error: {e}")
try:
PositiveInteger.check(-10) ## Should raise ValueError
except ValueError as e:
print(f"Error: {e}")
NonEmptyString.check('hello') ## Should return 'hello'
try:
NonEmptyString.check('') ## Should raise ValueError
except ValueError as e:
print(f"Error: {e}")
Lorsque vous exécutez ce code, vous devriez voir :
10
Error: Expected <class 'int'>
Error: Expected >= 0
'hello'
Error: Must be non-empty
Cela montre comment nous pouvons combiner des validateurs pour créer des règles de validation plus complexes.
Lorsque vous avez terminé de tester, vous pouvez quitter l'interpréteur Python en exécutant la commande suivante :
exit()
Application des validateurs à une classe Stock
Dans cette étape, nous allons voir comment nos validateurs fonctionnent dans une situation réelle. Les validateurs sont comme de petits vérificateurs qui s'assurent que les données que nous utilisons respectent certaines règles. Nous allons créer une classe Stock. Une classe est comme un modèle pour créer des objets. Dans ce cas, la classe Stock représentera une action boursière, et nous utiliserons nos validateurs pour nous assurer que les valeurs de ses attributs (comme le nombre de parts et le prix) sont valides.
Création de la classe Stock
Tout d'abord, nous devons créer un nouveau fichier. Dans le WebIDE, créez un nouveau fichier appelé stock.py. Ce fichier contiendra le code de notre classe Stock. Maintenant, ajoutez le code suivant au fichier stock.py :
## stock.py
from validate import PositiveInteger, PositiveFloat
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
self._shares = PositiveInteger.check(value)
@property
def price(self):
return self._price
@price.setter
def price(self, value):
self._price = PositiveFloat.check(value)
def cost(self):
return self.shares * self.price
Analysons ce que fait ce code :
- Nous commençons par importer les validateurs
PositiveIntegeretPositiveFloatdepuis notre modulevalidate. Ces validateurs nous aideront à nous assurer que le nombre de parts est un entier positif et que le prix est un nombre à virgule flottante positif. - Ensuite, nous définissons une classe
Stock. À l'intérieur de la classe, nous avons une méthode__init__. Cette méthode est appelée lorsque nous créons un nouvel objetStock. Elle prend trois paramètres :name,sharesetprice, et les assigne aux attributs de l'objet. - Nous utilisons des propriétés (properties) et des mutateurs (setters) pour valider les valeurs de
sharesetprice. Une propriété est un moyen de contrôler l'accès à un attribut, et un mutateur est une méthode qui est appelée lorsque nous essayons de définir la valeur de cet attribut. Lorsque nous définissons l'attributshares, la méthodePositiveInteger.checkest appelée pour nous assurer que la valeur est un entier positif. De même, lorsque nous définissons l'attributprice, la méthodePositiveFloat.checkest appelée pour nous assurer que la valeur est un nombre à virgule flottante positif. - Enfin, nous avons une méthode
cost. Cette méthode calcule le coût total de l'action en multipliant le nombre de parts par le prix.
Test de la classe Stock
Maintenant que nous avons créé notre classe Stock, nous devons la tester pour voir si les validateurs fonctionnent correctement. Ouvrez un nouveau terminal et lancez l'interpréteur Python. Vous pouvez le faire en exécutant la commande suivante :
python3
Une fois que l'interpréteur Python est en cours d'exécution, nous pouvons importer et tester notre classe Stock. Entrez le code suivant dans l'interpréteur Python :
from stock import Stock
## Create a valid stock
s = Stock('GOOG', 100, 490.10)
print(f"Name: {s.name}, Shares: {s.shares}, Price: {s.price}")
print(f"Cost: {s.cost()}")
## Try setting an invalid shares value
try:
s.shares = -10
except ValueError as e:
print(f"Error setting shares: {e}")
## Try setting an invalid price value
try:
s.price = "not a price"
except TypeError as e:
print(f"Error setting price: {e}")
Lorsque vous exécutez ce code, vous devriez voir une sortie similaire à ceci :
Name: GOOG, Shares: 100, Price: 490.1
Cost: 49010.0
Error setting shares: Expected >= 0
Error setting price: Expected <class 'float'>
Cette sortie montre que nos validateurs fonctionnent comme prévu. La classe Stock ne nous permet pas de définir des valeurs invalides pour shares et price. Lorsque nous essayons de définir une valeur invalide, une erreur est levée, et nous pouvons capturer et afficher cette erreur.
Comprendre l'aide de l'héritage
L'un des grands avantages de l'utilisation de nos validateurs est que nous pouvons facilement combiner différentes règles de validation. L'héritage est un concept puissant en Python qui nous permet de créer de nouvelles classes basées sur des classes existantes. Avec l'héritage multiple, nous pouvons utiliser la fonction super() pour appeler des méthodes de plusieurs classes parentes.
Par exemple, si nous voulons nous assurer que le nom de l'action n'est pas vide, nous pouvons suivre ces étapes :
- Importer le validateur
NonEmptyStringdepuis le modulevalidate. Ce validateur nous aidera à vérifier si le nom de l'action n'est pas une chaîne de caractères vide. - Ajouter un mutateur de propriété pour l'attribut
namedans la classeStock. Ce mutateur utilisera la méthodeNonEmptyString.check()pour valider le nom de l'action.
Cela montre comment l'héritage, en particulier l'héritage multiple avec la fonction super(), nous permet de construire des composants flexibles et réutilisables dans différentes combinaisons.
Lorsque vous avez terminé de tester, vous pouvez quitter l'interpréteur Python en exécutant la commande suivante :
exit()
Résumé
Dans ce laboratoire, vous avez appris le comportement de l'héritage en Python et maîtrisé plusieurs concepts clés. Vous avez exploré la différence entre l'héritage simple et l'héritage multiple, compris comment la fonction super() navigue dans l'ordre de résolution des méthodes (Method Resolution Order - MRO), appris à implémenter un héritage multiple coopératif et appliqué l'héritage pour construire un système de validation pratique.
Vous avez également créé un cadre de validation flexible en utilisant l'héritage et l'avez appliqué à un exemple concret avec la classe Stock, ce qui montre comment l'héritage peut créer des composants réutilisables et composables. Les points clés à retenir incluent le fonctionnement de super() dans l'héritage simple et multiple, la capacité de l'héritage multiple à composer des fonctionnalités et l'utilisation de mutateurs de propriétés avec des validateurs. Ces concepts sont fondamentaux pour la programmation orientée objet en Python et sont largement utilisés dans les applications réelles.