Décorateurs de classe
Fait partie de la section Programmation orientée objet du Journey Python de Coddy. Leçon 40 sur 64.
Les décorateurs de classes vous permettent de modifier ou d'améliorer des classes en les enveloppant avec une autre fonction. Ils fonctionnent comme les décorateurs de fonctions, mais s'appliquent à des classes entières.
Voici une classe simple sans décoration :
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Créez un décorateur de classe qui ajoute une nouvelle méthode :
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Ajoute la méthode à la classe
return clsAppliquez le décorateur à une classe à l’aide de @ :
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Maintenant, la classe possède à la fois les méthodes original et ajoutée :
person = EnhancedPerson("Alice")
print(person.greet()) # Bonjour, je m'appelle Alice
print(person.farewell()) # Au revoir de AliceVous pouvez également encapsuler une méthode existante : conserver l’originale afin de pouvoir toujours l’appeler à l’intérieur de l’encapsuleur. Cela est utile lorsque vous souhaitez ajouter un comportement (comme le suivi ou la journalisation) autour d’une méthode que la classe possède déjà :
def add_tracking(cls):
original_greet = cls.greet # Stocker la méthode originale
def tracked_greet(self):
print(f"greet was called") # Comportement supplémentaire avant
return original_greet(self) # Appeler la méthode originale
cls.greet = tracked_greet # Remplacer la méthode sur la classe
return cls
@add_tracking
class TrackedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Lorsque vous appelez greet maintenant, l’enveloppe s’exécute d’abord, puis délègue l’appel à l’original :
person = TrackedPerson("Alice")
print(person.greet())
# greet a été appelé
# Bonjour, je m'appelle AliceLes étapes clés pour envelopper une method existante sont les suivantes :
1. Enregistrer la method original : original = cls.method
2. Définir une nouvelle function qui appelle original(self) ainsi que toute logique supplémentaire.
3. Réaffecter la nouvelle function à la classe : cls.method = new_function
4. Retourner la classe modifiée.
Point clé : Les décorateurs de classe prennent une classe en entrée, la modifient ou l’améliorent, puis renvoient la classe modifiée. Ils peuvent ajouter des méthodes, modifier des attributs ou encapsuler des fonctionnalités existantes. Utilisez @decorator_name au-dessus de la définition de la classe pour les appliquer. Cela offre un moyen propre d’étendre les fonctionnalités d’une classe sans héritage.
Défi
MoyenDans ce défi, vous allez implémenter un décorateur de classe qui ajoute une fonctionnalité de suivi des appels de méthodes.
decorator.py- Implémentez le décorateur de classeadd_countercalculator.py- Contient les classes qui utiliseront votre décorateur
Le fichier driver.py contient de nombreux scénarios de test qui valideront votre implémentation avec différents cas d’utilisation, schémas d’héritage et cas limites. Vous n’avez pas besoin de modifier ce fichier, mais son examen vous aidera à comprendre le comportement attendu.
Essayez vous-même
from calculator import Calculator
# Gestionnaire complet de cas de test
test_case = input()
if test_case == "basic_functionality":
calc = Calculator()
print(calc.add(5, 3)) # Devrait afficher 8
print(calc.add(2, 7)) # Devrait afficher 9
print(calc.subtract(10, 4)) # Devrait afficher 6
print(calc.call_counts) # Should print {'add': 2, 'subtract': 1}
elif test_case == "multiple_instances":
calc1 = Calculator()
calc2 = Calculator()
print(calc1.add(5, 3)) # Devrait afficher 8
print(calc2.add(2, 7)) # Devrait afficher 9
print(calc1.subtract(10, 4)) # Devrait afficher 6
# Les deux instances devraient partager le même call_counts
print(calc1.call_counts) # Should print {'add': 2, 'subtract': 1}
print(calc2.call_counts) # Should print {'add': 2, 'subtract': 1}
print(calc1.call_counts is calc2.call_counts) # Devrait afficher True
elif test_case == "method_call_order":
calc = Calculator()
print(calc.add(1, 2)) # Devrait afficher 3
print(calc.subtract(5, 2)) # Devrait afficher 3
print(calc.add(10, 20)) # Devrait afficher 30
print(calc.add(7, 3)) # Devrait afficher 10
print(calc.call_counts) # Should print {'add': 3, 'subtract': 1}
elif test_case == "large_values":
calc = Calculator()
print(calc.add(1000000, 2000000)) # Devrait afficher 3000000
print(calc.subtract(5000000, 2000000)) # Devrait afficher 3000000
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "negative_values":
calc = Calculator()
print(calc.add(-5, -3)) # Devrait afficher -8
print(calc.subtract(-10, -4)) # Devrait afficher -6
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "float_values":
calc = Calculator()
print(calc.add(5.5, 3.2)) # Devrait afficher 8.7
print(calc.subtract(10.5, 4.2)) # Devrait afficher 6.3
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "zero_values":
calc = Calculator()
print(calc.add(0, 0)) # Devrait afficher 0
print(calc.subtract(0, 0)) # Devrait afficher 0
print(calc.call_counts) # Should print {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # Devrait afficher 8
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# Réinitialiser le compteur
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # Devrait afficher 9
print(calc.call_counts) # Should print {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # Devrait afficher 8
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# Manipuler le compteur directement
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # Devrait afficher 9
print(calc.call_counts) # Should print {'add': 101, 'subtract': 0}
elif test_case == "performance_test":
calc = Calculator()
# Effectuer 1000 opérations add
for i in range(1000):
calc.add(i, i+1)
# Effectuer 500 opérations subtract
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # Should print {'add': 1000, 'subtract': 500}Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaireEntraînez-vous par vous-même : Compilateur Python en ligne