Dekoratory w Pythonie

Dekoratory w Pythonie

Ten artykuł wyjaśnia dekoratory w Pythonie.

YouTube Video

Dekoratory w Pythonie

Dekoratory Pythona to potężna funkcja, która pozwala dodawać dodatkową funkcjonalność do funkcji lub metod. Dekoratory pozwalają dodać nową funkcjonalność bez modyfikowania istniejącego kodu, co zwiększa jego możliwość ponownego użycia i łatwość utrzymania.

Podstawy dekoratorów

Dekoratory w Pythonie działają poprzez przyjęcie funkcji jako argumentu i zwrócenie nowej funkcji z dodaną funkcjonalnością. Dzięki dekoratorom można łatwo dodać przed- lub post-przetwarzanie do funkcji.

Bez dekoratorów zmiana zachowania funkcji wymaga bezpośredniej edycji samej funkcji. Dzięki dekoratorom można rozszerzyć funkcjonalność bez modyfikowania pierwotnej implementacji.

Podstawowa struktura dekoratora

 1def my_decorator(func):
 2    def wrapper():
 3        print("Before the function call")
 4        func()
 5        print("After the function call")
 6    return wrapper
 7
 8@my_decorator
 9def say_hello():
10    print("Hello!")
11
12say_hello()

W tym przykładzie funkcja my_decorator działa jako dekorator, otaczając funkcję say_hello. Dekorator jest stosowany za pomocą składni @my_decorator, a kiedy say_hello() jest wywoływana, automatycznie wykonywane są działania wstępne i końcowe zapewnione przez dekorator.

Jak działają dekoratory

Dekoratory działają zgodnie z następującymi krokami.

  1. Dekorator przyjmuje funkcję (lub metodę) jako argument.

  2. Definiuje funkcję osłaniającą, aby wykonać oryginalną funkcję.

  3. Funkcja osłaniająca wykonuje dodatkowe przetwarzanie przed lub po wywołaniu oryginalnej funkcji.

  4. Dekorator zwraca funkcję osłaniającą. W rezultacie oryginalna funkcja zostaje zastąpiona nową, udekorowaną funkcją.

Dekoratory dla funkcji z argumentami

Podczas stosowania dekoratora do funkcji z argumentami funkcja osłaniająca musi zostać dostosowana do przyjęcia tych argumentów.

 1def my_decorator(func):
 2    def wrapper(*args, **kwargs):
 3        print("Before the function call")
 4        result = func(*args, **kwargs)
 5        print("After the function call")
 6        return result
 7    return wrapper
 8
 9@my_decorator
10def greet(name):
11    print(f"Hello, {name}!")
12
13greet("Alice")

Dzięki użyciu *args i **kwargs można obsługiwać funkcje przyjmujące dowolną liczbę argumentów i argumentów kluczowych. Pozwala to na stosowanie dekoratorów ogólnie do funkcji z dowolnym typem argumentów.

Przykłady Zastosowania Dekoratorów

Dekorator do Logowania

Dekoratory są często używane do dodania funkcjonalności logowania. Na przykład, tworząc dekorator, który zapisuje logi przed i po wykonaniu funkcji, możesz rejestrować, kiedy funkcja została wywołana i jak długo trwa jej wykonanie.

 1import time
 2
 3def log_time(func):
 4    def wrapper(*args, **kwargs):
 5        start_time = time.time()
 6        print(f"Calling function '{func.__name__}'...")
 7        result = func(*args, **kwargs)
 8        end_time = time.time()
 9        print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10        return result
11    return wrapper
12
13@log_time
14def long_task(duration):
15    time.sleep(duration)
16    print("Task completed!")
17
18long_task(2)

W tym przykładzie dekorator log_time mierzy czas wykonania funkcji i zapisuje go jako log. Funkcja long_task jest opakowana dekoratorem, a jej czas wykonania jest rejestrowany podczas działania.

Dekorator do Zarządzania Uprawnieniami

Dekoratory mogą być również używane do zarządzania uprawnieniami. Na przykład, możesz ograniczyć przetwarzanie, sprawdzając, czy użytkownik ma określone uprawnienia.

 1def requires_permission(user_role):
 2    def decorator(func):
 3        def wrapper(*args, **kwargs):
 4            if user_role != "admin":
 5                print("Permission denied!")
 6                return
 7            return func(*args, **kwargs)
 8        return wrapper
 9    return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13    print(f"User {user_id} has been deleted.")
14
15delete_user(123)  # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456)  # Permission denied!

Dekorator requires_permission ogranicza wykonanie funkcji na podstawie roli użytkownika. Ujednolicając taką logikę w dekoratorze, możesz zapobiec rozpraszaniu logiki zarządzania uprawnieniami w kodzie, poprawiając czytelność.

Zagnieżdżanie Dekoratorów

Można zastosować wiele dekoratorów. Kiedy wiele dekoratorów jest zastosowanych do jednej funkcji, wykonują się one w kolejności od góry do dołu.

 1def uppercase(func):
 2    def wrapper(*args, **kwargs):
 3        result = func(*args, **kwargs)
 4        return result.upper()
 5    return wrapper
 6
 7def exclaim(func):
 8    def wrapper(*args, **kwargs):
 9        result = func(*args, **kwargs)
10        return result + "!"
11    return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16    return f"Hello, {name}"
17
18print(greet("Alice"))  # "HELLO, ALICE!"

W tym przykładzie dwa dekoratory są zastosowane do funkcji greet. Dekorator @exclaim dodaje na końcu ciągu znaków wykrzyknik, a @uppercase zamienia ciąg znaków na wielkie litery.

Dekoratory dla Metod Klas

Dekoratory mogą być również używane z metodami klasowymi. Jest to szczególnie przydatne, gdy chcesz kontrolować zachowanie metod w obrębie klasy. Podczas stosowania dekoratorów do metod klasowych należy zwrócić uwagę na argumenty takie jak self lub cls.

 1def log_method_call(func):
 2    def wrapper(self, *args, **kwargs):
 3        print(f"Calling method '{func.__name__}'...")
 4        return func(self, *args, **kwargs)
 5    return wrapper
 6
 7class MyClass:
 8    @log_method_call
 9    def greet(self, name):
10        print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")

W tym przykładzie dekorator log_method_call jest zastosowany do metody greet, aby zapisać log podczas jej wywołania.

Uwagi dotyczące Dekoratorów

Istnieją pewne kwestie do rozważenia podczas używania dekoratorów. Dekoratory mogą potencjalnie zmienić nazwę i opis oryginalnej funkcji (np. __name__ lub __doc__), dlatego zaleca się używanie functools.wraps do zachowania tych informacji.

1import functools
2
3def my_decorator(func):
4    @functools.wraps(func)
5    def wrapper(*args, **kwargs):
6        print("Before the function call")
7        return func(*args, **kwargs)
8    return wrapper

Użycie @functools.wraps zapewnia poprawne przekazanie metadanych oryginalnej funkcji do funkcji opakowującej.

Podsumowanie

Dekoratory w języku Python to bardzo potężne narzędzie, które umożliwia zwięzłe dodawanie dodatkowej funkcjonalności do funkcji i metod. Mogą być stosowane w różnych scenariuszach, aby zmniejszyć duplikację kodu oraz poprawić jego łatwość utrzymania i ponownego wykorzystania. Rozumiejąc, jak działają dekoratory, możesz pisać bardziej wydajny i elastyczny kod.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video