You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 12:05:20