Dekorator klasy to funkcja, która przyjmuje klasę jako argument i zwraca albo zmodyfikowaną, albo zupełnie nową klasę. Dzięki nim można dynamicznie dodawać metody, zmieniać zachowanie istniejących metod, a nawet zwracać podklasę, która rozszerza funkcjonalność oryginalnej klasy.
def add_repr(cls): def __repr__(self): return f'<{cls.__name__}: {self.__dict__}>' cls.__repr__ = __repr__ return cls @add_repr class Point: def __init__(self, x, y): self.x = x self.y = y p = Point(3, 4) print(p) # <Point: {'x': 3, 'y': 4}>
add_repr dynamicznie dodaje metodę __repr__ do wszystkich klas, do których jest zastosowany.*args i **kwargs, które są używane podczas tworzenia instancji klasy.Co się stanie, jeśli zastosujesz dwa dekoratory klas jeden po drugim? Czy zawsze kolejność ich zastosowania jest oczywista?
Dekoratory są stosowane "od dołu do góry": najpierw stosowany jest dekorator bezpośrednio nad deklaracją klasy, a następnie następny wyżej, itd. Kolejność jest BARDZO WAŻNA, ponieważ wynik pierwszego dekoratora jest przekazywany do drugiego i tak dalej.
@dec1 @dec2 class Test: ... # Równoznaczne: # Test = dec1(dec2(Test))
Historia
W aplikacji e-commerce chcieli logować metody klasy, ale przypadkowo zwrócili nie ten obiekt z dekoratora, przez co metody utraciły cechę klasy, przez co przestały poprawnie dziedziczyć, łamiąc działanie modelu.
Historia
Na projekcie do autogeneracji nowych metod użyto dekoratora-dodawacza, ale zapomniano o super() przy zwracaniu podklasy. W rezultacie naruszono hierarchię oraz MRO, co doprowadziło do niewłaściwych wywołań klasy bazowej w klasach dziedziczących.
Historia
W pipeline danych próbowano owinąć klasy kilkoma dekoratorami (logger, tracker zmian), ale z powodu niewłaściwej kolejności zastosowania powstał konflikt nazw metod, co doprowadziło do błędów w produkcji z powodu "zgubionych" metod.