如何在类内部通过装饰器收集并调用@classmethod标注的方法
问题背景
我需要实现一个my_deco装饰器,用来收集回调函数并在指定时机执行。装饰普通函数时一切正常,在类定义完成后调用my_deco(C.cm)收集类方法也能正常执行,但在类内部装饰classmethod时遇到两个问题:
- 如何正确检测正在装饰的是
classmethod对象(目前用isinstance(cb, classmethod)判断,不确定是否正确) - 如何获取类
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
相关产品推荐
相关产品推荐

