Python中inspect.getmembers处理抛异常@property方法的异常行为
Python inspect.getmembers触发@property直接抛异常的问题
问题复现
import inspect class SomeClass: def __init__(self): inspect.getmembers(self, predicate=inspect.ismethod) def this_is_okay(self): raise Exception('an error that may be thrown') @property def this_is_not_okay(self): raise Exception('an error that may be thrown') @property def but_this_is_okay(self): if True: raise Exception('an error that may be thrown')
创建SomeClass实例时,会触发this_is_not_okay中抛出的异常,但普通方法不会(注:but_this_is_okay的getter被调用时实际也会抛异常,核心问题是@property的getter会被inspect.getmembers触发)。
原因
inspect.getmembers(self, predicate=inspect.ismethod)的执行逻辑是:
- 先遍历并获取实例的所有属性值,包括
@property定义的属性——获取这类属性值会直接调用其getter函数。 - 再用
inspect.ismethod过滤结果,只保留方法对象。
- 普通方法:获取到的是方法对象本身,不会执行方法体,所以即使方法内抛异常也不会触发。
@property属性:获取属性值时会直接执行getter,若getter在顶层直接抛异常,就会立即触发错误。
另外,inspect.getmembers默认按字母顺序返回结果,本身也不符合你「按类定义顺序获取方法」的需求。
解决方法
针对你按类定义顺序获取实例可调用方法的核心需求,推荐以下方案:
方案1:遍历类__dict__按定义顺序筛选(推荐)
Python 3.7+中,类的__dict__会保留成员的定义顺序,直接遍历类的成员,筛选出函数并绑定为实例方法:
import inspect class SomeClass: def __init__(self): # 按定义顺序收集实例方法 instance_methods = [] # 遍历类的成员而非实例,避免触发property getter for name, member in self.__class__.__dict__.items(): # 筛选类中定义的函数(未绑定方法) if inspect.isfunction(member): # 绑定为实例方法 bound_method = getattr(self, name) instance_methods.append(bound_method) # 验证结果:按定义顺序输出方法名 print([m.__name__ for m in instance_methods]) # 输出:['__init__', 'this_is_okay'] def this_is_okay(self): raise Exception('an error that may be thrown') @property def this_is_not_okay(self): raise Exception('an error that may be thrown') @property def but_this_is_okay(self): if True: raise Exception('an error that may be thrown')
这种方式完全不会触发@property的getter,同时严格保留方法的定义顺序。
方案2:自定义过滤规则避免触发property(不推荐,无法保留顺序)
如果一定要使用inspect.getmembers,可以先判断成员是否为property,跳过它们,但结果会按字母排序:
import inspect def filter_instance_methods(obj): # 先排除property对象,避免触发getter if isinstance(obj, property): return False return inspect.ismethod(obj) class SomeClass: def __init__(self): members = inspect.getmembers(self, predicate=filter_instance_methods) # 输出按字母序排列的方法名 print([name for name, _ in members]) # 输出:['__init__', 'this_is_okay'] # ... 其他方法同上
内容的提问来源于stack exchange,提问作者chocojunkie
相关产品推荐
相关产品推荐

