Jak używać dekoratorów Pythona do ulepszania funkcji

Dekoratory Pythona to potężny i elastyczny sposób modyfikowania lub ulepszania funkcji i metod. Dekoratory zapewniają sposób na opakowanie funkcji o dodatkową funkcjonalność, umożliwiając rozszerzenie jej zachowania bez modyfikowania jej faktycznego kodu. Ten artykuł wprowadzi Cię do koncepcji dekoratorów, sposobu ich tworzenia i używania oraz przedstawi kilka praktycznych przykładów.

Czym jest dekorator?

Dekorator to funkcja, która przyjmuje inną funkcję i rozszerza jej zachowanie bez jawnej modyfikacji. W Pythonie dekoratory są często używane do dodawania funkcjonalności, takiej jak rejestrowanie, kontrola dostępu lub pomiar wydajności, do istniejących funkcji lub metod. Dekoratory są stosowane do funkcji przy użyciu składni @decorator_name.

# Basic example of a decorator
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()

Jak działają dekoratorzy

Gdy zastosujesz dekorator do funkcji, Python wykonuje następujące kroki:

  1. Funkcja dekoratora jest wywoływana z oryginalną funkcją jako argumentem.
  2. Funkcja dekoratora definiuje nową funkcję (często nazywaną wrapper), która rozszerza lub modyfikuje zachowanie oryginalnej funkcji.
  3. Funkcja dekoratora zwraca nową funkcję.
  4. Gdy wywoływana jest funkcja dekorowana, w rzeczywistości wywołuje ona nową funkcję zwróconą przez dekorator.

Tworzenie prostego dekoratora

Utwórzmy prosty dekorator, który mierzy czas wykonania funkcji. Jest to przydatne do testowania wydajności i optymalizacji.

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Execution time: {end_time - start_time} seconds")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(2)
    print("Function finished!")

slow_function()

Używanie dekoratorów z argumentami

Czasami możesz chcieć przekazać argumenty do swojego dekoratora. Aby to osiągnąć, musisz utworzyć fabrykę dekoratorów — funkcję, która zwraca dekorator. Oto przykład dekoratora, który przyjmuje argument, aby określić niestandardową wiadomość.

def custom_message_decorator(message):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(message)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@custom_message_decorator("Starting the function...")
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

Dekoratory dla metod w klasach

Dekoratory mogą być również używane z metodami wewnątrz klas. Typowe zastosowania obejmują rejestrowanie wywołań metod, kontrolę dostępu i buforowanie wyników. Oto przykład użycia dekoratora do rejestrowania wywołań metod w klasie.

def log_method_call(method):
    def wrapper(self, *args, **kwargs):
        print(f"Calling {method.__name__} with arguments {args} and keyword arguments {kwargs}")
        return method(self, *args, **kwargs)
    return wrapper

class MyClass:
    @log_method_call
    def my_method(self, x, y):
        print(f"Result: {x + y}")

obj = MyClass()
obj.my_method(5, 7)

Dekoratory łańcuchowe

Możesz zastosować wiele dekoratorów do jednej funkcji. Są one stosowane od najbardziej wewnętrznego dekoratora do najbardziej zewnętrznego. Pozwala to na komponowanie różnych funkcjonalności razem. Oto przykład łączenia dwóch dekoratorów:

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def exclamation_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + "!"
    return wrapper

@exclamation_decorator
@uppercase_decorator
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))

Wniosek

Dekoratory to wszechstronne narzędzie w Pythonie, które pozwala na ulepszanie lub modyfikowanie zachowania funkcji i metod. Używając dekoratorów, możesz dodawać funkcjonalność wielokrotnego użytku w swojej bazie kodu bez zmiany podstawowej logiki swoich funkcji. Niezależnie od tego, czy chodzi o rejestrowanie, czas, czy modyfikowanie wyników, dekoratory pomagają utrzymać kod w czystości i łatwości konserwacji. Ćwicz używanie dekoratorów, aby stać się bardziej biegłym i wykorzystać ich moc w swoich projektach Pythona.