Decorator di classe
Fa parte della sezione Programmazione orientata agli oggetti del percorso Python di Coddy. Lezione 40 di 64.
I decoratori di classe ti consentono di modificare o migliorare le classi avvolgendole con un'altra funzione. Funzionano come i decoratori di funzione, ma vengono applicati a intere classi.
Ecco una semplice classe senza decoratore:
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Crea un decoratore di classe che aggiunga un nuovo metodo:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Aggiungi il metodo alla classe
return clsApplica il decoratore a una classe usando @:
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Ora la classe ha sia i metodi originali sia quelli aggiunti:
person = EnhancedPerson("Alice")
print(person.greet()) # Ciao, mi chiamo Alice
print(person.farewell()) # Un saluto da AlicePuoi anche avvolgere un metodo esistente: memorizza l'originale così puoi continuare a chiamarlo all'interno del wrapper. È utile quando vuoi aggiungere un comportamento (come il monitoraggio o la registrazione) attorno a un metodo già presente nella classe:
def add_tracking(cls):
original_greet = cls.greet # Memorizza il metodo originale
def tracked_greet(self):
print(f"greet was called") # Comportamento aggiuntivo prima della chiamata
return original_greet(self) # Chiama il metodo originale
cls.greet = tracked_greet # Sostituisci il metodo nella 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}"Quando chiami greet ora, il wrapper viene eseguito per primo, poi delega all’originale:
person = TrackedPerson("Alice")
print(person.greet())
# greet è stato chiamato
# Ciao, mi chiamo AliceI passaggi fondamentali per avvolgere un metodo esistente sono:
1. Salva il metodo originale: original = cls.method
2. Definisci una nuova funzione che chiama original(self) più eventuale logica aggiuntiva.
3. Riassegna la nuova funzione alla classe: cls.method = new_function
4. Restituisci la classe modificata.
Punto chiave: i decoratori di classe prendono una classe come input, la modificano o la potenziano e restituiscono la classe modificata. Possono aggiungere metodi, modificare attributi o racchiudere funzionalità esistenti. Usa @decorator_name sopra la definizione della classe per applicarli. Questo offre un modo semplice per estendere le funzionalità di una classe senza ricorrere all’ereditarietà.
Sfida
MedioIn questa sfida implementerai un decoratore di classe che aggiunge la funzionalità di tracciamento delle chiamate ai metodi.
decorator.py- Implementa il decoratore di classeadd_countercalculator.py- Contiene le classi che useranno il tuo decoratore
Il file driver.py contiene numerosi scenari di test che verificheranno la tua implementazione con vari casi d'uso, modelli di ereditarietà e casi limite. Non devi modificare questo file, ma esaminarlo ti aiuterà a capire il comportamento previsto.
Provalo tu
from calculator import Calculator
# Gestore completo dei casi di test
test_case = input()
if test_case == "basic_functionality":
calc = Calculator()
print(calc.add(5, 3)) # Dovrebbe stampare 8
print(calc.add(2, 7)) # Dovrebbe stampare 9
print(calc.subtract(10, 4)) # Dovrebbe stampare 6
print(calc.call_counts) # Dovrebbe stampare {'add': 2, 'subtract': 1}
elif test_case == "multiple_instances":
calc1 = Calculator()
calc2 = Calculator()
print(calc1.add(5, 3)) # Dovrebbe stampare 8
print(calc2.add(2, 7)) # Dovrebbe stampare 9
print(calc1.subtract(10, 4)) # Dovrebbe stampare 6
# Entrambe le istanze dovrebbero condividere lo stesso call_counts
print(calc1.call_counts) # Dovrebbe stampare {'add': 2, 'subtract': 1}
print(calc2.call_counts) # Dovrebbe stampare {'add': 2, 'subtract': 1}
print(calc1.call_counts is calc2.call_counts) # Dovrebbe stampare True
elif test_case == "method_call_order":
calc = Calculator()
print(calc.add(1, 2)) # Dovrebbe stampare 3
print(calc.subtract(5, 2)) # Dovrebbe stampare 3
print(calc.add(10, 20)) # Dovrebbe stampare 30
print(calc.add(7, 3)) # Dovrebbe stampare 10
print(calc.call_counts) # Dovrebbe stampare {'add': 3, 'subtract': 1}
elif test_case == "large_values":
calc = Calculator()
print(calc.add(1000000, 2000000)) # Dovrebbe stampare 3000000
print(calc.subtract(5000000, 2000000)) # Dovrebbe stampare 3000000
print(calc.call_counts) # Dovrebbe stampare {'add': 1, 'subtract': 1}
elif test_case == "negative_values":
calc = Calculator()
print(calc.add(-5, -3)) # Dovrebbe stampare -8
print(calc.subtract(-10, -4)) # Dovrebbe stampare -6
print(calc.call_counts) # Dovrebbe stampare {'add': 1, 'subtract': 1}
elif test_case == "float_values":
calc = Calculator()
print(calc.add(5.5, 3.2)) # Dovrebbe stampare 8.7
print(calc.subtract(10.5, 4.2)) # Dovrebbe stampare 6.3
print(calc.call_counts) # Dovrebbe stampare {'add': 1, 'subtract': 1}
elif test_case == "zero_values":
calc = Calculator()
print(calc.add(0, 0)) # Dovrebbe stampare 0
print(calc.subtract(0, 0)) # Dovrebbe stampare 0
print(calc.call_counts) # Dovrebbe stampare {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # Dovrebbe stampare 8
print(calc.call_counts) # Dovrebbe stampare {'add': 1, 'subtract': 0}
# Reimposta il contatore
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # Dovrebbe stampare 9
print(calc.call_counts) # Dovrebbe stampare {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # Dovrebbe stampare 8
print(calc.call_counts) # Dovrebbe stampare {'add': 1, 'subtract': 0}
# Modifica direttamente il contatore
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # Dovrebbe stampare 9
print(calc.call_counts) # Dovrebbe stampare {'add': 101, 'subtract': 0}
elif test_case == "performance_test":
calc = Calculator()
# Esegui 1000 operazioni add
for i in range(1000):
calc.add(i, i+1)
# Esegui 500 operazioni subtract
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # Dovrebbe stampare {'add': 1000, 'subtract': 500}Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP
File esterniIntroduzione a OOPClassi vs. oggettiIl parametro selfMetodiAttributiMetodo costruttore (__init__)Riepilogo - Calcolatrice semplice4Ereditarietà
Ereditarietà di baseLa funzione super()Sovrascrittura dei metodiEreditarietà multiplaOrdine di risoluzione dei metodiRiepilogo - Gerarchia dei dipendenti7Metodi speciali
Introduzione ai metodi magiciSovraccarico degli operatoriMetodi magici dei contenitoriRiepilogo - Lista personalizzata10Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello ObserverModello Strategy2Decoratori
Introduzione ai decoratoriDecoratore di proprietàDecoratore di metodo staticoDecoratore di metodo di classe5Polimorfismo
Ripasso dell'override dei metodiDuck typingClassi e metodi astrattiProgettazione delle interfacceRiepilogo - Calcolatore di forme8Concetti avanzati di OOP
Composizione vs ereditarietàMixinMetodi statici e di classeDecorator di classeContext manager11Pattern di progettazione - Parte 2
Pattern dei comandiPattern adattatorePattern decoratorePattern metodo templatePattern statoPattern composito3Proprietà delle classi
Variabili di istanza e di classeDecorator delle proprietàAttributi privatiRiepilogo - Gestore di conti bancari6Incapsulamento
Membri pubblici, protetti e privatiModificatori di accessoOccultamento delle informazioniDecorator delle proprietà: avanzatiRipasso - Sistema di registri degli studenti12Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e UtenteSistema di prestitoFunzionalità di ricercaInterfaccia amministratoreTest e integrazioneEsercitati da solo: Compilatore Python online