在Python编程中,当我们使用点号(.)访问对象的属性时,比如 obj.name,Python内部会按照一套严格的顺序来查找这个属性。理解这套Python属性访问顺序对于编写健壮、可维护的代码至关重要,尤其是在使用高级特性如描述符、__getattr__ 或元类时。

当你执行 obj.attr 时,Python会按以下顺序尝试获取属性值:
__get__ 和 __set__ 的描述符)obj.__dict__)__get__ 的描述符,或普通方法/变量)__getattr__ 方法(如果前面都找不到,且定义了该方法)这个顺序体现了Python的“描述符优先”原则:数据描述符 > 实例属性 > 非数据描述符/类属性。
- 数据描述符:同时定义了 __get__ 和 __set__ 方法的类。
- 非数据描述符:只定义了 __get__ 方法(例如普通函数)。
数据描述符拥有最高优先级,即使实例字典中有同名属性,也会被忽略。
__getattribute__ 的作用所有属性访问都会首先触发 __getattribute__ 方法(除非你直接调用 object.__getattribute__)。它是整个属性查找流程的入口点。这也是为什么我们要小心重写它——容易造成无限递归。
class DataDescriptor: def __get__(self, obj, objtype=None): return "来自数据描述符" def __set__(self, obj, value): pass # 必须有 __set__ 才是数据描述符class MyClass: attr = DataDescriptor()obj = MyClass()obj.__dict__['attr'] = "来自实例字典"print(obj.attr) # 输出:来自数据描述符尽管我们在实例字典中设置了 attr,但由于类中存在同名的数据描述符,Python优先使用描述符的值。
class NonDataDescriptor: def __get__(self, obj, objtype=None): return "来自非数据描述符" # 没有 __set__,所以是非数据描述符class MyClass: attr = NonDataDescriptor()obj = MyClass()obj.__dict__['attr'] = "来自实例字典"print(obj.attr) # 输出:来自实例字典这次因为是非数据描述符,实例字典中的值优先级更高。
__getattr__ 作为最后手段class MyClass: def __getattr__(self, name): return f"{name} 未找到,由 __getattr__ 返回"obj = MyClass()print(obj.unknown_attr) # 输出:unknown_attr 未找到,由 __getattr__ 返回__getattribute__ 时务必调用 super().__getattribute__,否则会阻断正常属性查找。__getattribute__ 中访问其他属性,容易引发递归错误。掌握 Python属性访问顺序 能帮助你更好地理解框架(如Django ORM、SQLAlchemy)的工作原理,也能写出更灵活的类设计。记住核心口诀:数据描述符 > 实例属性 > 非数据描述符/类属性 > 父类 > __getattr__。
无论你是初学者还是进阶开发者,理解这套机制都能让你对Python有更深的认识。希望这篇教程能帮你理清 Python属性查找机制 的脉络!
本文由主机测评网于2025-12-22发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/20251211548.html