如何优雅标记类中需被外部类处理的成员函数集合
解决方法:给类方法打标记并收集到实例列表中
这个问题确实很常见——装饰器在方法定义阶段执行,这时候还没有实例,自然拿不到self。不过有几种优雅的方式可以解决,下面给你详细说明:
方案一:给方法打标记,实例初始化时扫描收集
最直观的思路是让装饰器给目标方法添加一个标记属性,然后在类的__init__方法里扫描所有方法,把带标记的方法加入实例的_marked列表。
代码实现
def mark(func): # 给被装饰的方法添加一个标记属性 func._is_marked = True return func class Tool: def __init__(self): self._marked = [] # 遍历实例的所有属性,筛选出带标记的方法 for attr_name in dir(self): attr = getattr(self, attr_name) # 确保是可调用的方法,并且带有我们定义的标记 if callable(attr) and hasattr(attr, '_is_marked') and attr._is_marked: self._marked.append(attr) @mark def func1(self): # 注意:这里必须加self参数,否则调用会报错 """I will be processed""" def func2(self): """I will not be processed""" def marked_funcs(self): """return all marked functions""" return self._marked
使用示例
t = Tool() print([func.__name__ for func in t.marked_funcs()]) # 输出: ['func1']
这个方案简单易懂,适合小型类;缺点是每次实例化都要扫描一遍属性,类方法多的时候会有轻微的性能损耗。
方案二:用元类在类定义阶段收集标记方法
元类可以在类被定义的时候就完成标记方法的收集,避免每次实例化都扫描。我们可以在元类里收集所有带标记的方法,存在类属性中,实例化时直接复制到实例的_marked列表。
代码实现
def mark(func): func._is_marked = True return func class MarkedMethodMeta(type): def __new__(cls, name, bases, attrs): # 在类定义阶段,收集所有带标记的方法 marked_methods = [ v for k, v in attrs.items() if callable(v) and hasattr(v, '_is_marked') ] # 把收集结果存为类属性 attrs['_marked_methods'] = marked_methods return super().__new__(cls, name, bases, attrs) class Tool(metaclass=MarkedMethodMeta): def __init__(self): # 实例化时直接复用类层面收集好的结果 self._marked = self._marked_methods.copy() @mark def func1(self): """I will be processed""" def func2(self): """I will not be processed""" def marked_funcs(self): return self._marked
这个方案效率更高,适合方法较多的类;缺点是需要了解元类的基本用法,对新手来说稍显复杂。
方案三:用类装饰器收集标记方法
如果不想用元类,可以用类装饰器来实现类似的效果——在类定义完成后,自动收集标记方法并修改类的__init__方法,让实例初始化时自动获取标记列表。
代码实现
def mark(func): func._is_marked = True return func def collect_marked_methods(cls): # 收集类中所有带标记的方法 cls._marked_methods = [ v for k, v in cls.__dict__.items() if callable(v) and hasattr(v, '_is_marked') ] # 重写类的__init__方法,添加标记列表的初始化逻辑 original_init = cls.__init__ def new_init(self, *args, **kwargs): original_init(self, *args, **kwargs) self._marked = self._marked_methods.copy() cls.__init__ = new_init return cls @collect_marked_methods class Tool: def __init__(self): # 原来的初始化逻辑可以保留,装饰器会自动补充标记列表的初始化 pass @mark def func1(self): """I will be processed""" def func2(self): """I will not be processed""" def marked_funcs(self): return self._marked
这个方案不需要涉及元类,代码更易读,适合对元类不太熟悉的开发者。
注意事项
不管用哪种方案,都要记得给类方法加上self参数——你原来的示例代码里func1()和func2()都没加self,这会导致调用方法时报错,一定要修正。
内容的提问来源于stack exchange,提问作者zsnjuts
相关产品推荐
相关产品推荐

