如何在Python ratelimit装饰器中引用类常量(无需self)?
问题:在ratelimit装饰器中引用类常量的方法
我使用Python的ratelimit库通过装饰器实现函数限流,想把类常量UNIVERSALIS_MAX_CALLS_PER_SECOND传入装饰器,但无法用self引用它。希望避免全局变量,是否有办法在装饰器中引用这个常量,或者更简洁合适的处理方式?
代码示例:
class Universalis(): # FIXME: hard-coded North America API_ENDPOINT = "http://universalis.app/api/v2/North-America/" UNIVERSALIS_MAX_CALLS_PER_SECOND = 13 # 25 max UNIVERSALIS_MAX_CONNECTIONS = 6 # 8 max LISTINGS_PER_API_CALL = 500 # Universalis no max. @sleep_and_retry @limits(calls=self.UNIVERSALIS_MAX_CALLS_PER_SECOND, period=1) def fetch_and_process_item_listings(self, item_ids_querystring): # magic...
方案1:直接通过类名访问常量
类常量在类定义阶段就已经存在,装饰器初始化时可以直接用类名来引用,这是最简单直接的方式:
class Universalis(): API_ENDPOINT = "http://universalis.app/api/v2/North-America/" UNIVERSALIS_MAX_CALLS_PER_SECOND = 13 # 25 max UNIVERSALIS_MAX_CONNECTIONS = 6 # 8 max LISTINGS_PER_API_CALL = 500 # Universalis no max. @sleep_and_retry @limits(calls=Universalis.UNIVERSALIS_MAX_CALLS_PER_SECOND, period=1) def fetch_and_process_item_listings(self, item_ids_querystring): # magic...
完全不需要额外逻辑,也不会引入全局变量,适合大多数场景。
方案2:自定义动态装饰器工厂(用自省实现)
如果不想硬编码类名,或者需要更灵活的属性获取,可以写一个装饰器工厂,在函数调用时通过自省动态获取类属性:
from functools import wraps from ratelimit import limits, sleep_and_retry def dynamic_limit(calls_attr, period=1): def decorator(func): @wraps(func) def wrapper(self, *args, **kwargs): # 通过self获取所属类,再用getattr拿到常量值 call_limit = getattr(self.__class__, calls_attr) # 动态构造限流装饰器并执行原函数 limited_func = limits(calls=call_limit, period=period)(func) return limited_func(self, *args, **kwargs) return wrapper return decorator # 使用方式 class Universalis(): API_ENDPOINT = "http://universalis.app/api/v2/North-America/" UNIVERSALIS_MAX_CALLS_PER_SECOND = 13 # 25 max UNIVERSALIS_MAX_CONNECTIONS = 6 # 8 max LISTINGS_PER_API_CALL = 500 # Universalis no max. @sleep_and_retry @dynamic_limit(calls_attr="UNIVERSALIS_MAX_CALLS_PER_SECOND", period=1) def fetch_and_process_item_listings(self, item_ids_querystring): # magic...
这种方式利用了Python的自省特性,不需要绑定固定类名,适合类可能被继承或者重命名的场景。
方案3:将限流逻辑移到类初始化中
如果装饰器的静态初始化限制了灵活性,也可以在类的__init__方法中创建带限流的方法实例,这样就能正常用self访问类常量:
from ratelimit import limits, sleep_and_retry class Universalis(): API_ENDPOINT = "http://universalis.app/api/v2/North-America/" UNIVERSALIS_MAX_CALLS_PER_SECOND = 13 # 25 max UNIVERSALIS_MAX_CONNECTIONS = 6 # 8 max LISTINGS_PER_API_CALL = 500 # Universalis no max. def __init__(self): # 初始化时创建带限流的内部方法实例 limited_fetch = limits(calls=self.UNIVERSALIS_MAX_CALLS_PER_SECOND, period=1)(self._fetch_and_process) self.limited_fetch_with_retry = sleep_and_retry(limited_fetch) def _fetch_and_process(self, item_ids_querystring): # 原方法的核心逻辑 # magic... def fetch_and_process_item_listings(self, item_ids_querystring): # 对外暴露的方法,调用带限流的实例 return self.limited_fetch_with_retry(item_ids_querystring)
这种方式把装饰器的初始化时机从类定义阶段移到实例化阶段,自然就能访问self的属性。
内容的提问来源于stack exchange,提问作者Chris Cummings
相关产品推荐
相关产品推荐

