Могу ли я передать динамическое значение декоратору в Python?

Я немного новичок в Python и его концепции. Для моего текущего проекта мне нужно сделать определенные вызовы API на скорости х / мин. В связи с этим я наткнулся на концепцию декоратора и библиотеки Python для одного и того же. Это называется ratelimit и нажмите здесь, чтобы перейти к ссылке на github.

Простейший пример для этого API:

from ratelimit import rate_limited
import requests

MESSAGES=100
SECONDS= 3600

@rate_limited(MESSAGES, SECONDS)
def call_api(url):
    response = requests.get(url)

   if response.status_code != 200:
     raise ApiError('Cannot call API: {}'.format(response.status_code))
   return response

Но мне нужно вызвать эту функцию call_api из другой функции

def send_message():
    global MESSAGES
    global SECONDS
    MESSAGES=10
    SECONDS=5
    end_time=time.time()+60 #the end time is 60 seconds from the start time
    while(time.time()<end_time):
        call_api(url)

Я хочу, чтобы вызов состоялся, и хочу, чтобы аргументы декоратора обновлялись во время выполнения, поскольку реальные значения будут вводиться пользователем. Но, насколько я понимаю, декоратор принимает значение до запуска. Так как я могу передать динамическое значение декоратору.

Спасибо заранее за помощь

2 ответа

Декоратор может использоваться как угодно, а не только когда вы определяете свою функцию. Вы просто не можете использовать синтаксис декоратора.

# undecorated
def call_api(url):
    response = requests.get(url)
    if response.status_code != 200:
        raise ApiError('Cannot call API: {}'.format(response.status_code))
    return response

def send_message():
    global MESSAGES
    global SECONDS
    MESSAGES=10
    SECONDS=5
    end_time=time.time()+60 #the end time is 60 seconds from the start time

    rl_api = rate_limited(MESSAGES, SECONDS)(call_api)
    while(time.time()<end_time):
        rl_api(url)

Это означает, что вы можете создавать несколько функций с ограниченной скоростью одновременно, используя разные аргументы для rate_limited,

fast_api = rate_limited(100, 5)(call_api)
slow_api = rate_limited(10, 5)(call_api)

Ваш вопрос заключается в том, можете ли вы вызывать декораторы по ссылке, а не по значению. На это ответ да. Краткое содержание: передать изменяемый объект.

В данном конкретном случае это не принесет вам пользы. Как вы можете видеть в коде для ratelimit модуль, два параметра, every а также period, используются для установки новой переменной, frequencyкогда определяется декорированная функция:

frequency = abs(every) / float(clamp(period))

Чтобы получить переменную частоту, вам нужно будет переписать модуль для поддержки ваших потребностей, но это должно быть выполнимо. Рассмотрим следующее в качестве минимальной иллюстрации:

def limit(limiter):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(limiter.period, limiter.every)
            return func(*args, **kwargs)
        return wrapper
    return decorator


class Limiter():
    def __init__(self, every, period):
        self.every = every
        self.period = period

Теперь раскрутить:

>>> l = Limiter(1, 2)
>>> 
>>> @limit(l)
... def foo():
...     pass
... 
>>> foo()
2 1
>>> l.every = 10
>>> foo()
2 10
Другие вопросы по тегам