Посібник із декораторів Python для досвідчених програмістів

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

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

Декоратори — це функції, які змінюють поведінку іншої функції. Вони обертають іншу функцію, щоб розширити її поведінку без явної зміни її коду. Декоратори визначаються за допомогою синтаксису @decorator_name і розміщуються над визначенням функції.

Базовий синтаксис декоратора

Простий декоратор приймає функцію як аргумент, визначає внутрішню функцію, яка додає деяку поведінку, а потім повертає внутрішню функцію.

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

Функції декоратора з аргументами

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

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

Декоратори гніздування

Декоратори можуть бути вкладеними, щоб поєднати кілька видів поведінки. Наприклад, ми можемо використовувати два або більше декораторів для однієї функції.

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

Класові декоратори

У Python декоратори також можуть бути реалізовані як класи за допомогою методу __call__. Декоратори на основі класів корисні, коли вам потрібно складніше управління станом і поведінка.

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

Використання functools.wraps для збереження метаданих

Коли ви пишете декоратори, оформлена функція втрачає свої оригінальні метадані, такі як ім’я та рядок документації. Декоратор functools.wraps можна використовувати для копіювання метаданих оригінальної функції до функції-огортки.

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.

Висновок

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