如何在元类中判断类的方法是实例方法还是静态方法?
区分元类中的实例方法与静态方法的更优方案
你用检查第一个参数是否为self的方式确实有点脆弱——毕竟Python对参数名没有强制要求,有人会用this甚至别的名字,甚至偶尔会看到不写第一个参数的实例方法(虽然不规范,但语法上允许)。这里有几个更可靠的方案,都是基于Python标准库的,不用手动遍历继承树:
方案一:直接通过类型判断
从类上获取属性时,静态方法、类方法和实例方法的类型是不同的:
- 静态方法是
staticmethod类的实例,需要通过__func__拿到内部的函数 - 类方法是
classmethod类的实例 - 实例方法则是普通的
function对象(还没绑定到实例)
利用这个特性,我们可以修改元类的代码:
import inspect class DecoratedMeta(type): def __new__(mcs, name, bases, attrs): cls = super().__new__(mcs, name, bases, attrs) for attr_name in dir(cls): attr = getattr(cls, attr_name) # 处理静态方法 if isinstance(attr, staticmethod): func = attr.__func__ if mcs.is_valid(func): # 装饰后要重新包装成staticmethod setattr(cls, attr_name, staticmethod(mcs.decorate(func))) # 处理实例方法(排除特殊方法和类方法) elif inspect.isfunction(attr) and not isinstance(attr, classmethod): # 可选:排除双下划线开头的特殊方法 if not attr_name.startswith('__') and mcs.is_valid(attr): setattr(cls, attr_name, mcs.decorate(attr)) return cls @staticmethod def is_valid(func): # 这里写你的判断逻辑,比如排除某些函数 return True @staticmethod def decorate(func): def wrapper(*args, **kwargs): print(f"Decorated: {func.__name__}") return func(*args, **kwargs) return wrapper
方案二:用inspect.getmembers过滤属性
inspect.getmembers可以帮你直接过滤出指定类型的成员,代码会更简洁:
import inspect class DecoratedMeta(type): def __new__(mcs, name, bases, attrs): cls = super().__new__(mcs, name, bases, attrs) # 获取所有实例方法(普通函数,非静态/类方法) for attr_name, func in inspect.getmembers(cls, predicate=inspect.isfunction): if not attr_name.startswith('__') and mcs.is_valid(func): setattr(cls, attr_name, mcs.decorate(func)) # 获取所有静态方法 for attr_name, static_meth in inspect.getmembers(cls, predicate=lambda x: isinstance(x, staticmethod)): func = static_meth.__func__ if mcs.is_valid(func): setattr(cls, attr_name, staticmethod(mcs.decorate(func))) return cls # is_valid 和 decorate 方法同上
为什么这两个方案更好?
- 避免了参数名的依赖:不管实例方法的第一个参数叫
self还是this,都能准确识别 - 自动处理继承的属性:
dir(cls)和inspect.getmembers都会包含基类的成员,不用手动遍历继承树 - 类型判断更准确:直接利用Python的装饰器类型(
staticmethod/classmethod)来区分,逻辑更清晰
内容的提问来源于stack exchange,提问作者Campi
相关产品推荐
相关产品推荐

