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

类内装饰器调用报错:如何在MetabaseManager类中正确使用私有装饰器?

解决类内私有装饰器装饰方法时的staticmethod不可调用错误

错误原因

你遇到的TypeError: 'staticmethod' object is not callable核心问题是装饰器应用顺序错误:如果将@staticmethod放在私有装饰器@_renewSessionIfExpired上方,装饰器会接收staticmethod对象而非原始函数——而staticmethod本身的调用逻辑不符合装饰器的预期,因此触发报错。

另外需要注意:staticmethod无法访问实例或类的属性,根本没法检查会话状态,因此需要访问会话的方法不应该用staticmethod,应改用实例方法或类方法。

正确实现方案

方案1:装饰实例方法(推荐,会话状态绑定到实例)

将getQuestion定义为实例方法,私有装饰器直接修饰原始方法,包装函数通过self访问实例的会话状态。

import time

class MetabaseManager:
    def __init__(self):
        self.session_token = None
        self.session_expires_at = 0

    def _renewSessionIfExpired(func):
        def wrapper(self, *args, **kwargs):
            # 检查会话是否过期
            if not self.session_token or time.time() > self.session_expires_at:
                # 调用Metabase API续期会话
                self.session_token = self._fetch_new_session()
                self.session_expires_at = time.time() + 3600  # 设为1小时有效期
            return func(self, *args, **kwargs)
        return wrapper

    def _fetch_new_session(self):
        # 模拟获取新会话的API调用
        return f"instance_token_{int(time.time())}"

    @_renewSessionIfExpired
    def getQuestion(self, question_id):
        # 实际调用Metabase API获取问题
        return {
            "question_id": question_id,
            "used_token": self.session_token,
            "content": "Metabase question content"
        }

方案2:装饰类方法(会话状态全局共享)

如果所有实例共享同一会话,可以使用类方法,注意装饰顺序:先应用私有装饰器,再用@classmethod修饰。

import time

class MetabaseManager:
    _session_token = None
    _session_expires_at = 0

    def _renewSessionIfExpired(func):
        def wrapper(cls, *args, **kwargs):
            if not cls._session_token or time.time() > cls._session_expires_at:
                cls._session_token = cls._fetch_new_session()
                cls._session_expires_at = time.time() + 3600
            return func(cls, *args, **kwargs)
        return wrapper

    @classmethod
    def _fetch_new_session(cls):
        return f"class_token_{int(time.time())}"

    @classmethod
    @_renewSessionIfExpired
    def getQuestion(cls, question_id):
        return {
            "question_id": question_id,
            "used_token": cls._session_token,
            "content": "Shared Metabase question content"
        }

关键注意事项

  1. 装饰顺序优先级:使用@classmethod时,必须将私有装饰器放在下方,确保装饰器先处理原始函数,再转换为类方法。
  2. 方法类型适配:需要访问会话状态的方法禁止使用staticmethod,否则无法读取/更新会话信息。
  3. Python 3.6兼容性:上述代码完全兼容Python 3.6,嵌套函数形式的装饰器在该版本中可正常运行。

内容的提问来源于stack exchange,提问作者Amir Shabani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:43:11