【问题标题】:Can i pass a dynamic value to a decorator in python?我可以将动态值传递给python中的装饰器吗?
【发布时间】:2018-03-13 15:12:35
【问题描述】:

我对 python 及其概念有点陌生。对于我当前的项目,我需要以 x 速率/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)

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

提前感谢您的帮助

【问题讨论】:

    标签: python decorator rate-limiting


    【解决方案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)
    

    【讨论】:

    • 它像魅力一样工作。谢谢一吨。今天真的学到了一些有用的东西。:)
    【解决方案2】:

    您的问题基本上是您是否可以通过引用而不是通过值来调用装饰器。答案是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
    

    【讨论】:

    • Python 从不通过引用调用
    猜你喜欢
    • 2017-12-13
    • 2017-11-22
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 2018-12-04
    相关资源
    最近更新 更多