Dekorator to funkcja, która opakowuje inną funkcję, aby dodać zachowanie bez modyfikacji oryginalnego kodu. Jest stosowany za pomocą składni @decorator nad funkcją — czysty sposób na dodanie zagadnień krzyżowych (ang. cross-cutting concerns) takich jak logowanie, timing, cachowanie lub kontrola dostępu.
Jak działają dekoratory
def my_decorator(func):
def wrapper(*args, **kwargs): # wraps the original function
print("before")
result = func(*args, **kwargs) # call the original
print("after")
return result
return wrapper # return the wrapped version
@my_decorator
def greet(name):
print(f"Hello {name}")
greet("Ann")
# Output: before / Hello Ann / after
Syntaksja @my_decorator jest cukrem dla greet = my_decorator(greet) — zastępuje greet za pomocą wrapper, która wykonuje dodatkowy kod wokół oryginalnego wywołania. *args, **kwargs sprawia, że wrapper działa dla dowolnej sygnatury funkcji.
Praktyczny przykład: timing
import time, functools
def timed(func):
@functools.wraps(func) # preserves func's name/docstring (important!)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.perf_counter() - start:.3f}s")
return result
return wrapper
@timed
def slow_task():
time.sleep(1)
@functools.wraps(func) jest ważny — bez niego opakowana funkcja traci swoją oryginalną nazwę i docstring.
Dekoratory z argumentami
def repeat(times): # outer takes the decorator's argument
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(times=3)
def hello(): print("hi") # prints "hi" three times
Dekorator sparametryzowany potrzebuje dodatkowej warstwy (funkcja zwracająca dekorator).
Typowe dekoratory wbudowane / biblioteczne
@property → turn a method into a computed attribute
@staticmethod / @classmethod → method types
@functools.lru_cache → memoize results
@app.route("/") → web framework routing (Flask)
@pytest.fixture → test fixtures
Dlaczego to ważne
Dekoratory to potężna, charakterystycznie pythonowa funkcja do dodawania ponownie używanego zachowania (logowanie, timing, cachowanie, autentykacja, walidacja, routing) do funkcji w czysty i deklaratywny sposób — oddzielając zagadnienia krzyżowe od logiki głównej.
Są wszędzie w prawdziwym Pythonie: frameworkach webowych (@app.route), cachowaniu (@lru_cache), klasach (@property) i testowaniu.
Zrozumienie, jak je pisać (funkcja wrapper, *args/**kwargs, functools.wraps) oraz forma sparametryzowana pozwala na poprawne używanie dekoratorów ekosystemu i budowanie własnych wielokrotnie używalnych abstrakcji.
