Декораторы с аргументами в Python

В этой статье вы узнаете, как использовать декораторы с аргументами и что такое фабрики декораторов.

Предположим, у нас есть функция say(), которая выводит какое-то сообщение:

def say(message):
    ''' выводит сообщение
    Аргументы
        message: сообщение, которое нужно вывести
    '''
    print(message)

и мы хотим выполнять эту функцию 5 раз, каждый раз когда вызываем ее. Например, если мы вызовем функцию с таким аргументом say("Привет"), вывод должен быть таким:

Привет
Привет
Привет
Привет
Привет 

Это можно сделать с помощью простого декоратора:

@repeat
def say(message):
    ''' выводит сообщение
    Аргументы
        message: сообщение, которое нужно вывести
    '''
    print(message)

Для этого просто нужно создать этот декоратор. Например, вот так:

def repeat(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        for _ in range(5):
            result = fn(*args, **kwargs)
        return result

    return wrapper    

Вот весь код:

from functools import wraps

def repeat(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        for _ in range(5):
            result = fn(*args, **kwargs)
        return result

    return wrapper


@repeat
def say(message):
    ''' выводит сообщение
    Аргументы
        message: сообщение, которое нужно вывести
    '''
    print(message)

Но что если нужно выполнить функцию say() 10 раз? В этом случае придется менять захардкоженное значение 5 в декораторе repeat

Такое решение — негибкое. Например, если вы захотите использовать декоратор repeat для выполнения функции 5 раз, а потом — 10 раз. Декоратор repeat так сделать не позволит.

Чтобы исправить это, нужно изменить декоратор repeat так, чтобы он принимал аргумент, задающий количество раз, которое должна выполниться функция, следующим образом:

@repeat(5)
def say(message):
    ...

Чтобы определить декоратор repeat, repeat(5) должен возвращать исходный декоратор.

def repeat(times):
    # возвращает оригинальный декоратор repeat

Новая функция repeat возвращает декоратор. Такие функции часто называют фабрикой декораторов.

Следующая функция repeat возвращает декоратор:

def repeat(times):
    ''' вызывает функцию количество раз, равное times '''
    def decorate(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = fn(*args, **kwargs)
            return result
        return wrapper
    return decorate

В этом коде функция decorate() является декоратором. Она эквивалентна оригинальному декоратору repeat.

Обратите внимание, что новая функция repeat() не является декоратором. Это фабрика декораторов, которая возвращает декоратор.

Соберем все вместе:

from functools import wraps


def repeat(times):
    ''' вызывает функцию количество раз, равное times '''
    def decorate(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = fn(*args, **kwargs)
            return result
        return wrapper
    return decorate


@repeat(10)
def say(message):
    ''' выводит сообщение
    Аргументы
        message: сообщение, которое нужно вывести
    '''
    print(message)


say('Привет')
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©