我可以将动态值传递给python中的装饰器吗?

时间:2018-03-13 15:12:36

标签: python decorator ratelimit

我对python及其概念有点新鲜。对于我目前的项目,我需要以x rate / y分钟进行某些api调用。 关于这一点,我遇到了装饰器的概念和相同的python库。 它被称为ratelimit并点击here转到其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)

我希望调用发生,并希望装饰器的参数在运行时更新,因为实际值将是用户输入。 但根据我的理解,装饰器在运行时之前取值。那么如何将动态值传递给装饰器。

Thanx提前帮助

2 个答案:

答案 0 :(得分:1)

装饰器可以随时使用,而不仅仅是在定义函数时。您无法使用装饰器语法

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

答案 1 :(得分:0)

你的问题基本上是你是否可以通过引用而不是通过值来调用装饰器。答案是yes。执行摘要:传递一个可变对象。

在这种特殊情况下,它对你没有任何好处。正如您在ratelimit模块的code中所看到的,everyperiod这两个参数用于设置新变量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