Як використовувати декоратори Python для покращення функцій

Декоратори Python — це потужний і гнучкий спосіб модифікувати або вдосконалювати функції та методи. Декоратори забезпечують спосіб обернути функцію додатковими функціональними можливостями, дозволяючи вам розширити її поведінку, не змінюючи її фактичний код. Ця стаття познайомить вас з концепцією декораторів, як їх створювати та використовувати, а також розгляне деякі практичні приклади.

Що таке декоратор?

Декоратор — це функція, яка приймає іншу функцію та розширює її поведінку, не змінюючи її явно. У Python декоратори часто використовуються для додавання таких функцій, як ведення журналів, контроль доступу або вимірювання продуктивності до існуючих функцій або методів. Декоратори застосовуються до функцій за допомогою синтаксису @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()

Як працюють декоратори

Коли ви застосовуєте декоратор до функції, Python по суті виконує такі кроки:

  1. Функція декоратора викликається з оригінальною функцією як аргументом.
  2. Функція декоратора визначає нову функцію (часто її називають wrapper), яка покращує або змінює поведінку вихідної функції.
  3. Функція декоратора повертає нову функцію.
  4. Коли декорована функція викликається, вона фактично викликає нову функцію, повернуту декоратором.

Створення простого декоратора

Давайте створимо простий декоратор, який вимірює час виконання функції. Це корисно для тестування продуктивності та оптимізації.

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()

Використання декораторів з аргументами

Іноді ви можете передати аргументи вашому декоратору. Щоб досягти цього, вам потрібно створити фабрику декораторів — функцію, яка повертає декоратор. Ось приклад декоратора, який приймає аргумент для вказівки спеціального повідомлення.

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")

Декоратори для методів у класах

Декоратори також можна використовувати з методами всередині класів. Загальне використання включає виклики методів журналювання, контроль доступу та кешування результатів. Ось приклад використання декоратора для реєстрації викликів методів у класі.

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)

Ланцюгові декоратори

Ви можете застосувати кілька декораторів до однієї функції. Вони наносяться від самого внутрішнього декоратора до зовнішнього. Це дозволяє комбінувати різні функції разом. Ось приклад об’єднання двох декораторів:

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"))

Висновок

Декоратори — це універсальний інструмент у Python, який дозволяє покращувати або змінювати поведінку функцій і методів. Використовуючи декоратори, ви можете додавати функціональні можливості для багаторазового використання у свою кодову базу, не змінюючи основну логіку своїх функцій. Декоратори допомагають підтримувати ваш код чистим і придатним для обслуговування, незалежно від того, чи йдеться про журналювання, синхронізацію чи модифікацію виводу. Попрактикуйтесь у використанні декораторів, щоб стати більш досвідченими та використати їхні можливості у своїх проектах Python.