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

如何在类内部通过装饰器收集并调用@classmethod标注的方法

问题背景

我需要实现一个my_deco装饰器,用来收集回调函数并在指定时机执行。装饰普通函数时一切正常,在类定义完成后调用my_deco(C.cm)收集类方法也能正常执行,但在类内部装饰classmethod时遇到两个问题:

  1. 如何正确检测正在装饰的是classmethod对象(目前用isinstance(cb, classmethod)判断,不确定是否正确)
  2. 如何获取类C本身:此时的classmethod处于“未绑定”状态,调用时需要手动传入cls参数,但不知道在my_deco实现中怎么拿到C

示例代码:

# 用于收集被装饰回调函数的列表
cbs = []

# 标记回调函数并将其收集的装饰器
def my_deco(cb):
    cbs.append(cb)
    return cb

class C:
    @my_deco
    @classmethod
    def cm(cls):
        ...

# 执行收集到的回调函数
for cb in cbs:
    cb()
解决方案

1. 检测classmethod对象的正确方式

你用isinstance(cb, classmethod)的判断完全正确。classmethod是Python内置的类型,直接用isinstance就能准确识别被装饰的对象是否为类方法。

2. 获取类对象的可行方案

在类定义过程中执行装饰器时,类还未创建完成,无法直接获取类对象。这里提供两种实用思路:

方案一:借助元类介入类创建过程

元类可以在类被创建的阶段介入操作,能直接拿到类对象,适合统一处理类内的类方法:

cbs = []

class CallbackMeta(type):
    def __new__(cls, name, bases, attrs):
        # 遍历类的所有属性,筛选类方法
        for attr_val in attrs.values():
            if isinstance(attr_val, classmethod):
                # 提取类方法的原始函数
                original_func = attr_val.__func__
                # 包装一个自动传入类对象的回调
                def wrapper():
                    return original_func(new_cls)
                cbs.append(wrapper)
        # 创建并返回类对象
        new_cls = super().__new__(cls, name, bases, attrs)
        return new_cls

# 使用自定义元类定义类
class C(metaclass=CallbackMeta):
    @classmethod
    def cm(cls):
        print(f"执行类方法cm,所属类:{cls}")

# 执行收集到的回调
for cb in cbs:
    cb()

方案二:延迟绑定,类定义完成后再处理

让装饰器先记录待处理的类方法,等类创建完成后再绑定类对象:

cbs = []
# 临时存储待绑定的类方法原始函数
pending_methods = []

def my_deco(cb):
    if isinstance(cb, classmethod):
        # 先记录类方法的原始函数,暂不收集
        pending_methods.append(cb.__func__)
        return cb
    else:
        # 普通函数直接收集
        cbs.append(cb)
        return cb

class C:
    @my_deco
    @classmethod
    def cm(cls):
        print(f"执行类方法cm,所属类:{cls}")

# 类定义完成后,处理待绑定的方法
for func in pending_methods:
    # 通过函数的限定名找到所属类
    class_name = func.__qualname__.split('.')[0]
    cls_obj = globals()[class_name]
    # 包装成可直接调用的回调
    def wrapper():
        return func(cls_obj)
    cbs.append(wrapper)

# 执行回调
for cb in cbs:
    cb()

补充:直接调用未绑定的classmethod

如果能明确知道类的名称,也可以在执行回调时手动传入类对象,但灵活性较低:

# 已知类为C时的调用方式
for cb in cbs:
    if isinstance(cb, classmethod):
        cb.__func__(C)
    else:
        cb()

内容的提问来源于stack exchange,提问作者Vito De Tullio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:42:08