Python:如何安全查找属性且不触发类或元类的__getattribute__?
如何安全查找属性,不触发类或元类的__getattribute__方法?
需要一种无需try-catch的100%安全方式,用于查找Python对象或类型的属性,要求完全不触发类或元类的__getattribute__方法。原本认为object.__getattribute__是安全的,但实际调用时抛出了AttributeError,报错信息如下:
Traceback (most recent call last): File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\tricks.py", line 49, in update_class_instances if isinstance(obj, old_class): File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\system\path.py", line 104, in __instancecheck__ return cls.__subclasscheck__(instance.__class__) File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\system\path.py", line 107, in __subclasscheck__ return pathlib_to_override(cls) in pathlib_to_override(subclass).__mro__ File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\logger_util.py", line 471, in __getattribute__ return getattr(instance, attr_name) File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\logger_util.py", line 524, in __getattribute__ if isinstance(e, AttributeError) and object.__getattribute__(self, "__getattr__") is not None: AttributeError: 'ExampleClass' object has no attribute '__getattr__'
随后尝试type.__getattribute__也出现类似错误:
Traceback (most recent call last): File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\tricks.py", line 83, in debug_reload_pymodules update_class_instances(old_attribute, new_attribute) File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\tricks.py", line 52, in update_class_instances RobustRootLogger.exception(f"Error updating class '{old_class.__class__.__name__}'") File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\logger_util.py", line 471, in __getattribute__ return getattr(instance, attr_name) File "C:\GitHub\PyKotor\Libraries\Utility\src\utility\logger_util.py", line 524, in __getattribute__ if isinstance(e, AttributeError) and type.__getattribute__(our_type, "__getattr__") is not None: AttributeError: type object 'ExampleClass' has no attribute '__getattr__'
注:当前正在研究Python内部机制,涉及importlib、__import__和sys.modules。
安全属性查找方案
要完全绕过自定义__getattribute__的拦截,需直接操作对象的底层属性存储(字典),避免触发任何自定义属性访问逻辑。以下是实现思路:
实例属性查找:
- 通过
object.__getattribute__(obj, '__dict__')获取实例自身的属性字典(此调用仅触发object基类的原生逻辑,不会触发子类自定义的__getattribute__)。 - 若实例因使用
__slots__没有__dict__,则直接遍历其类的MRO链查找属性。
- 通过
类属性查找:
- 遍历类的MRO(方法解析顺序)链,依次检查每个基类的
__dict__字典。
- 遍历类的MRO(方法解析顺序)链,依次检查每个基类的
以下是无需调用方额外加try-catch的实现(内部少量try-catch用于处理无__dict__的情况):
def safe_getattr(obj, attr_name, default=None): # 处理实例对象 if not isinstance(obj, type): # 尝试获取实例自身的属性字典 try: obj_dict = object.__getattribute__(obj, '__dict__') if attr_name in obj_dict: return obj_dict[attr_name] except AttributeError: # 实例无__dict__(如使用__slots__),跳过直接查类 pass # 获取实例所属的类 cls = type(obj) else: # 处理类/元类对象 cls = obj # 遍历类的MRO链查找属性 for base in cls.__mro__: base_dict = object.__getattribute__(base, '__dict__') if attr_name in base_dict: return base_dict[attr_name] # 找不到属性返回默认值 return default
说明
- 此方法完全绕过自定义的
__getattribute__,仅依赖Python原生逻辑获取属性字典,不会触发任何自定义逻辑。 - 属性不存在时直接返回指定默认值,无需调用方处理异常。
- 支持实例、类、元类等所有Python对象的属性查找。
内容的提问来源于stack exchange,提问作者Benjamin Auquite Wizard
相关产品推荐
相关产品推荐

