Przewodnik po dekoratorach Pythona dla zaawansowanych programistów
Dekoratory Pythona to potężne i elastyczne narzędzie do modyfikowania zachowania funkcji lub metod. Umożliwiają programistom rozszerzanie lub zmienianie funkcjonalności obiektów wywoływalnych w czysty, czytelny i łatwy w utrzymaniu sposób. W tym artykule omówiono zaawansowane koncepcje związane z dekoratorami Pythona, w tym zagnieżdżone dekoratory, argumenty dekoratora i dekoratory oparte na klasach.
Czym są dekoratory?
Dekoratory to funkcje, które modyfikują zachowanie innej funkcji. Owijają inną funkcję, aby rozszerzyć jej zachowanie bez jawnej modyfikacji jej kodu. Dekoratory są definiowane za pomocą składni @decorator_name
i są umieszczane nad definicją funkcji.
Podstawowa składnia dekoratora
Prosty dekorator przyjmuje funkcję jako argument, definiuje funkcję wewnętrzną, która dodaje pewne zachowanie, a następnie zwraca funkcję wewnętrzną.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Funkcje dekoratora z argumentami
Dekoratory mogą być bardziej elastyczne, akceptując argumenty. Aby utworzyć taki dekorator, musisz napisać funkcję, która zwraca dekorator. Umożliwia to dodanie bardziej dynamicznego zachowania do dekoratorów.
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Dekoratory zagnieżdżające
Dekoratory mogą być zagnieżdżane, aby łączyć wiele zachowań. Na przykład możemy używać dwóch lub więcej dekoratorów w jednej funkcji.
def uppercase_decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def repeat_decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result + result
return wrapper
@repeat_decorator
@uppercase_decorator
def say_word(word):
return word
print(say_word("hello"))
Dekoratory oparte na klasach
W Pythonie dekoratory mogą być również implementowane jako klasy za pomocą metody __call__
. Dekoratory oparte na klasach są przydatne, gdy potrzebujesz bardziej złożonego zarządzania stanem i zachowaniem.
class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__!r}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
Używanie functools.wraps
do zachowania metadanych
Gdy piszesz dekoratory, udekorowana funkcja traci swoje oryginalne metadane, takie jak nazwa i docstring. Dekorator functools.wraps
może być użyty do skopiowania metadanych oryginalnej funkcji do funkcji opakowującej.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Wrapper function executed before", func.__name__)
return func(*args, **kwargs)
return wrapper
@my_decorator
def display_info(name, age):
"""Displays name and age."""
print(f"display_info ran with arguments ({name}, {age})")
print(display_info.__name__) # Output: display_info
print(display_info.__doc__) # Output: Displays name and age.
Wniosek
Dekoratory Pythona to potężna funkcja, która umożliwia elastyczne projektowanie kodu i modyfikację zachowania. Zaawansowane użycie, takie jak zagnieżdżone dekoratory, dekoratory z argumentami i dekoratory oparte na klasach, może zapewnić jeszcze większą funkcjonalność i czytelność programów Pythona. Dzięki zrozumieniu i prawidłowemu wykorzystaniu dekoratorów programiści mogą pisać bardziej zwięzły, wydajny i czytelny kod.