programowanieBackend Developer

Czym są dekoratory klas w Pythonie? Jak za ich pomocą można modyfikować lub uzupełniać funkcjonalność klas? Podaj przykład i opisz możliwe niuanse.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

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.

Najprostszy przykład dekoratora 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}>
  • Dekorator add_repr dynamicznie dodaje metodę __repr__ do wszystkich klas, do których jest zastosowany.

Nuanse:

  • Dekoratory klas mogą zwracać nie tylko zagnieżdżenia w klasie, ale także całkowicie owinięte (na przykład klasy proxy lub dziedziczące).
  • Można napotkać błędy, jeśli dekorator nie obsługuje argumentów *args i **kwargs, które są używane podczas tworzenia instancji klasy.

Pytanie z podstępem.

Co się stanie, jeśli zastosujesz dwa dekoratory klas jeden po drugim? Czy zawsze kolejność ich zastosowania jest oczywista?

Odpowiedź:

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))

Przykłady rzeczywistych błędów z powodu braku znajomości niuansów tematu.


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.