Introduction
Dans ce laboratoire (lab), vous allez apprendre à connaître les métaclasses en Python. En Python, tout, y compris les classes, est un objet. Une métaclasse est une classe qui crée d'autres classes, offrant un moyen puissant de personnaliser la création de classes.
Les objectifs de ce laboratoire sont de comprendre ce qu'est une métaclasse, de créer votre première métaclasse, de l'utiliser pour créer de nouvelles classes et d'observer comment les métaclasses affectent l'héritage de classe. Le fichier mymeta.py sera créé pendant le laboratoire.
Comprendre les métaclasses
Les métaclasses sont une fonctionnalité avancée mais puissante en Python. En tant que débutant, vous vous demandez peut - être ce que sont les métaclasses et pourquoi elles sont importantes. Avant de commencer à créer notre première métaclasse, prenons un moment pour comprendre ces concepts.
Qu'est - ce qu'une métaclasse ?
En Python, tout est un objet, y compris les classes. Tout comme une classe normale est utilisée pour créer des instances, une métaclasse est utilisée pour créer des classes. Par défaut, Python utilise la métaclasse intégrée type pour créer toutes les classes.
Découpons le processus de création de classe étape par étape :
- Tout d'abord, Python lit la définition de classe que vous avez écrite dans votre code. C'est là que vous définissez le nom de la classe, ses attributs et ses méthodes.
- Ensuite, Python collecte des informations importantes sur la classe, telles que le nom de la classe, les classes de base dont elle hérite et tous ses attributs.
- Après cela, Python transmet ces informations collectées à la métaclasse. La métaclasse est chargée de prendre ces informations et de créer l'objet de classe réel.
- Enfin, la métaclasse crée et retourne la nouvelle classe.
Une métaclasse vous donne le pouvoir de personnaliser ce processus de création de classe. Vous pouvez modifier ou inspecter les classes pendant leur création, ce qui peut être très utile dans certains scénarios.
Visualisons cette relation pour faciliter la compréhension :
Metaclass → creates → Class → creates → Instance
Dans ce laboratoire (lab), nous allons créer notre propre métaclasse. En faisant cela, vous pourrez voir ce processus de création de classe en action et mieux comprendre le fonctionnement des métaclasses.
Créer votre première métaclasse
Maintenant, nous allons créer notre toute première métaclasse. Avant de commencer à coder, comprenons ce qu'est une métaclasse. En Python, une métaclasse est une classe qui crée d'autres classes. C'est comme un modèle pour les classes. Lorsque vous définissez une classe en Python, Python utilise une métaclasse pour créer cette classe. Par défaut, Python utilise la métaclasse type. Dans cette étape, nous allons définir une métaclasse personnalisée qui affiche des informations sur la classe qu'elle crée. Cela nous aidera à comprendre le fonctionnement interne des métaclasses.
Ouvrez VSCode dans le WebIDE et créez un nouveau fichier appelé
mymeta.pydans le répertoire/home/labex/project. C'est là que nous allons écrire notre code pour la métaclasse.Ajoutez le code suivant au fichier :
## mymeta.py
class mytype(type):
@staticmethod
def __new__(meta, name, bases, __dict__):
print("Creating class :", name)
print("Base classes :", bases)
print("Attributes :", list(__dict__))
return super().__new__(meta, name, bases, __dict__)
class myobject(metaclass=mytype):
pass
Analysons ce que ce code fait :
- Tout d'abord, nous définissons une nouvelle classe nommée
mytypequi hérite detype. Étant donné quetypeest la métaclasse par défaut en Python, en héritant d'elle, nous créons notre propre métaclasse personnalisée. - Ensuite, nous redéfinissons la méthode
__new__. En Python, la méthode__new__est une méthode spéciale qui est appelée lors de la création d'un nouvel objet. Dans le contexte d'une métaclasse, elle est appelée lors de la création d'une nouvelle classe. - À l'intérieur de notre méthode
__new__, nous affichons quelques informations sur la classe en cours de création. Nous affichons le nom de la classe, ses classes de base et ses attributs. Après cela, nous appelons la méthode__new__du parent en utilisantsuper().__new__(meta, name, bases, __dict__). Cela est important car c'est cela qui crée effectivement la classe. - Enfin, nous créons une classe de base nommée
myobjectet spécifions qu'elle doit utiliser notre métaclasse personnaliséemytype.
La méthode __new__ prend les paramètres suivants :
meta: Cela fait référence à la métaclasse elle - même. Dans notre cas, c'estmytype.name: C'est le nom de la classe en cours de création.bases: C'est un tuple contenant les classes de base dont la nouvelle classe hérite.__dict__: C'est un dictionnaire qui contient les attributs de la classe.
- Enregistrez le fichier en appuyant sur Ctrl+S ou en cliquant sur Fichier > Enregistrer. Enregistrer le fichier garantit que votre code est conservé et peut être exécuté plus tard.
Utiliser votre métaclasse
Maintenant, nous allons créer une classe qui utilise notre métaclasse par héritage. Cela nous aidera à comprendre comment la métaclasse est appelée lorsque la classe est définie.
Une métaclasse en Python est une classe qui crée d'autres classes. Lorsque vous définissez une classe, Python utilise une métaclasse pour construire l'objet de cette classe. En utilisant l'héritage, nous pouvons spécifier quelle métaclasse une classe doit utiliser.
- Ouvrez le fichier
mymeta.pyet ajoutez le code suivant à la fin du fichier :
class Stock(myobject):
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
def cost(self):
return self.shares * self.price
def sell(self, nshares):
self.shares -= nshares
Ici, nous définissons une classe Stock qui hérite de myobject. La méthode __init__ est une méthode spéciale dans les classes Python. Elle est appelée lorsque un objet de la classe est créé et est utilisée pour initialiser les attributs de l'objet. La méthode cost calcule le coût total des actions, et la méthode sell réduit le nombre d'actions.
Enregistrez le fichier en appuyant sur Ctrl+S. Enregistrer le fichier garantit que les modifications que vous avez apportées sont enregistrées et peuvent être exécutées plus tard.
Maintenant, exécutons le fichier pour voir ce qui se passe. Ouvrez un terminal dans le WebIDE et exécutez :
cd /home/labex/project
python3 mymeta.py
La commande cd change le répertoire de travail actuel en /home/labex/project, et python3 mymeta.py exécute le script Python mymeta.py.
Vous devriez voir une sortie similaire à ceci :
Creating class : myobject
Base classes : ()
Attributes : ['__module__', '__qualname__', '__doc__']
Creating class : Stock
Base classes : (<class '__main__.myobject'>,)
Attributes : ['__module__', '__qualname__', '__init__', 'cost', 'sell', '__doc__']
Cette sortie montre que notre métaclasse est invoquée lorsque les classes myobject et Stock sont créées. Remarquez comment :
- Pour
Stock, les classes de base incluentmyobjectcarStockhérite demyobject. - La liste des attributs inclut toutes les méthodes que nous avons définies (
__init__,cost,sell) ainsi que certains attributs par défaut.
- Interagissons avec notre classe
Stock. Créez un nouveau fichier nommétest_stock.pyavec le contenu suivant :
## test_stock.py
from mymeta import Stock
## Create a new Stock instance
apple = Stock("AAPL", 100, 154.50)
## Use the methods
print(f"Stock: {apple.name}, Shares: {apple.shares}, Price: ${apple.price}")
print(f"Total cost: ${apple.cost()}")
## Sell some shares
apple.sell(10)
print(f"After selling 10 shares: {apple.shares} shares remaining")
print(f"Updated cost: ${apple.cost()}")
Dans ce code, nous importons la classe Stock du module mymeta. Ensuite, nous créons une instance de la classe Stock nommée apple. Nous utilisons les méthodes de la classe Stock pour afficher des informations sur les actions, calculer le coût total, vendre quelques actions, puis afficher les informations mises à jour.
- Exécutez ce fichier pour tester notre classe
Stock:
python3 test_stock.py
Vous devriez voir une sortie comme celle - ci :
Creating class : myobject
Base classes : ()
Attributes : ['__module__', '__qualname__', '__doc__']
Creating class : Stock
Base classes : (<class 'mymeta.myobject'>,)
Attributes : ['__module__', '__qualname__', '__init__', 'cost', 'sell', '__doc__']
Stock: AAPL, Shares: 100, Price: $154.5
Total cost: $15450.0
After selling 10 shares: 90 shares remaining
Updated cost: $13905.0
Remarquez que les informations de notre métaclasse sont affichées en premier, suivies de la sortie de notre script de test. Cela s'explique par le fait que la métaclasse est invoquée lorsque la classe est définie, ce qui se produit avant l'exécution du code dans le script de test.
Explorer l'héritage des métaclasses
Les métaclasses ont une caractéristique fascinante : elles sont « collantes ». Cela signifie qu'une fois qu'une classe utilise une métaclasse, toutes ses sous - classes dans la hiérarchie d'héritage utiliseront également la même métaclasse. En d'autres termes, la propriété de la métaclasse se propage à travers la chaîne d'héritage.
Voyons cela en action :
- Tout d'abord, ouvrez le fichier
mymeta.py. À la fin de ce fichier, nous allons ajouter une nouvelle classe. Cette classe, nomméeMyStock, héritera de la classeStock. La méthode__init__est utilisée pour initialiser les attributs de l'objet, et nous appelons la méthode__init__de la classe parente en utilisantsuper().__init__pour initialiser les attributs communs. La méthodeinfoest utilisée pour retourner une chaîne formatée avec des informations sur les actions. Ajoutez le code suivant :
class MyStock(Stock):
def __init__(self, name, shares, price, category):
super().__init__(name, shares, price)
self.category = category
def info(self):
return f"{self.name} ({self.category}): {self.shares} shares at ${self.price}"
Après avoir ajouté le code, enregistrez le fichier
mymeta.py. Enregistrer le fichier garantit que les modifications que nous avons apportées sont enregistrées et peuvent être utilisées plus tard.Maintenant, nous allons créer un nouveau fichier nommé
test_inheritance.pypour tester le comportement d'héritage de la métaclasse. Dans ce fichier, nous allons importer la classeMyStockdepuis le fichiermymeta.py. Ensuite, nous allons créer une instance de la classeMyStock, appeler ses méthodes et afficher les résultats pour voir comment la métaclasse fonctionne via l'héritage. Ajoutez le code suivant àtest_inheritance.py:
## test_inheritance.py
from mymeta import MyStock
## Create a MyStock instance
tech_stock = MyStock("MSFT", 50, 305.75, "Technology")
## Test the methods
print(tech_stock.info())
print(f"Total cost: ${tech_stock.cost()}")
## Sell some shares
tech_stock.sell(5)
print(f"After selling: {tech_stock.shares} shares remaining")
print(f"Updated cost: ${tech_stock.cost()}")
- Enfin, exécutez le fichier
test_inheritance.pypour voir la métaclasse en action via l'héritage. Ouvrez votre terminal, accédez au répertoire où se trouve le fichiertest_inheritance.pyet exécutez la commande suivante :
python3 test_inheritance.py
Vous devriez voir une sortie similaire à :
Creating class : myobject
Base classes : ()
Attributes : ['__module__', '__qualname__', '__doc__']
Creating class : Stock
Base classes : (<class 'mymeta.myobject'>,)
Attributes : ['__module__', '__qualname__', '__init__', 'cost', 'sell', '__doc__']
Creating class : MyStock
Base classes : (<class 'mymeta.Stock'>,)
Attributes : ['__module__', '__qualname__', '__init__', 'info', '__doc__']
MSFT (Technology): 50 shares at $305.75
Total cost: $15287.5
After selling: 45 shares remaining
Updated cost: $13758.75
Remarquez que même si nous n'avons pas spécifié explicitement une métaclasse pour la classe MyStock, la métaclasse est toujours appliquée. Cela démontre clairement comment les métaclasses se propagent via l'héritage.
Utilisations pratiques des métaclasses
Dans notre exemple, la métaclasse ne fait que afficher des informations sur les classes. Cependant, les métaclasses ont de nombreuses applications pratiques dans la programmation réelle :
- Validation : Vous pouvez utiliser des métaclasses pour vérifier si une classe a les méthodes ou les attributs requis. Cela permet de s'assurer que les classes répondent à certains critères avant d'être utilisées.
- Enregistrement : Les métaclasses peuvent enregistrer automatiquement les classes dans un registre. Cela est utile lorsque vous avez besoin de suivre toutes les classes d'un certain type.
- Respect des interfaces : Elles peuvent être utilisées pour s'assurer que les classes implémentent les interfaces requises. Cela contribue à maintenir une structure cohérente dans votre code.
- Programmation orientée aspect : Les métaclasses peuvent ajouter des comportements aux méthodes. Par exemple, vous pouvez ajouter une journalisation ou une surveillance des performances aux méthodes sans modifier directement le code de la méthode.
- Systèmes ORM : Dans les systèmes de mappage objet - relationnel (ORM) comme Django ou SQLAlchemy, les métaclasses sont utilisées pour mapper les classes aux tables de base de données. Cela simplifie les opérations de base de données dans votre application.
Les métaclasses sont très puissantes, mais elles doivent être utilisées avec modération. Comme l'a dit un jour Tim Peters (connnu pour le Zen de Python) : « Les métaclasses sont une magie plus profonde que 99 % des utilisateurs n'auront jamais à se soucier. »
Résumé
Dans ce laboratoire (lab), vous avez appris ce que sont les métaclasses et comment elles fonctionnent en Python. Vous avez créé avec succès votre première métaclasse personnalisée pour surveiller la création de classes et l'avez utilisée pour générer de nouvelles classes. De plus, vous avez observé comment les métaclasses se propagent à travers les hiérarchies d'héritage.
Les métaclasses sont une fonctionnalité avancée de Python qui offre un contrôle sur la création de classes. Bien que vous n'ayez peut - être pas besoin de créer des métaclasses tous les jours, comprendre ce concept vous permet de mieux saisir le système d'objets de Python et ouvre des possibilités puissantes pour le développement de frameworks et de bibliothèques. Pour en savoir plus, explorez la documentation officielle de Python et les livres avancés sur la métaprogrammation en Python.