programowanieProgramista Backend

Wyjaśnij, jak działają dekoratory w Pythonie. Jaka jest ich główna użyteczność, jak są realizowane i jakie niuanse mogą wystąpić podczas ich użycia?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Dekoratory to funkcje, które przyjmują inną funkcję i zwracają nową funkcję o rozszerzonym lub zmodyfikowanym zachowaniu. Są one często używane do logowania, sprawdzania uprawnień, buforowania, pomiaru czasu wykonania i innych.

Dekoratory są realizowane za pomocą zamknięć (closure) lub klas implementujących metodę __call__. Klasyczna składnia:

# Prosty dekorator def simple_decorator(func): def wrapper(*args, **kwargs): print("Przed wywołaniem funkcji") result = func(*args, **kwargs) print("Po wywołaniu funkcji") return result return wrapper @simple_decorator def my_func(): print("Podstawowa funkcja") my_func()

W rezultacie wykona się tak:

Przed wywołaniem funkcji
Podstawowa funkcja
Po wywołaniu funkcji

Główna użyteczność — enkapsulacja powtarzającej się logiki poza główną logiką biznesową.

Niuanse:

  • Bez użycia functools.wraps traci się nazwę pierwotnej funkcji i jej docstring.
  • Jeśli dekorator przyjmuje argumenty, to pisze się potrójną zagnieżdżoną funkcję.

Pytanie z podstępem

Pytanie: "Jeśli udekoruję funkcję dekoratorem, a następnie spróbuję uzyskać nazwę funkcji przez __name__, co zobaczę i jak zachować oryginalną nazwę?"

Odpowiedź:

Domyślnie nazwa zmieni się na nazwę owijki (zwykle wrapper). Aby zachować oryginalne metadane, użyj functools.wraps:

import functools def dec(f): @functools.wraps(f) def wrapper(*args): return f(*args) return wrapper @dec def foo(): pass print(foo.__name__) # wypisze 'foo', a nie 'wrapper'

Przykłady rzeczywistych błędów z powodu nieznajomości niuansów tematu


Historia

W dużej automatyzacji zasobów, po owinięciu funkcji dekoratorami, system automatycznych testów oparty na refleksji przez nazwy funkcji testowych przestał działać. Problem tkwił w braku functools.wraps.


Historia

Dekorator, który dodaje logowanie, nie obsługiwał funkcji o różnych sygnaturach, ponieważ nie użyto *args, **kwargs. Niektóre funkcje po prostu przestały działać Silent Fail.


Historia

W projekcie z autoryzacją w REST API programista zaimplementował dekorator z parametrami, ale zapomniał poprawnie zagnieździć funkcje (było zagnieżdżenie dwupoziomowe zamiast trzech). W rezultacie dekorator nie mógł przyjmować parametrów.