You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python底层访问对象及类属性的具体执行顺序是怎样的?

Python属性访问完整执行流程

你的现有认知大部分符合Python的实际逻辑,但存在一处关键顺序偏差,也遗漏了非数据描述符、类属性访问的特殊规则,下面是精确到步骤的执行逻辑:


实例属性访问(obj.attr形式)的标准流程

所有实例属性访问的统一入口是实例所属类的__getattribute__方法(默认由顶层基类object提供实现,自定义重写时如果要保留默认查找逻辑需要调用super().__getattribute__,否则极易出现递归死循环),默认实现的查找顺序严格按以下优先级从高到低执行:

  • 查找类MRO链上的数据描述符
    沿着类的方法解析顺序(MRO,即类继承关系的线性化顺序)遍历所有父类和当前类的__dict__,查找是否存在名为attr的属性:
    • 如果找到的对象是数据描述符(即同时实现了__get__和__set__/__delete__方法的对象,内置的property就是最常见的数据描述符),直接调用该描述符的__get__方法返回结果,终止整个查找流程。

    这是你之前认知需要修正的核心点:数据描述符的查找早于实例字典检查,不存在「先查对象字典再查类字典」的通用规则,数据描述符可以完全覆盖实例字典里的同名属性。

  • 查找实例自身的属性字典
    如果上一步没有找到匹配的数据描述符,直接访问当前实例的__dict__,如果字典中存在attr键,直接返回对应的值,终止查找。
  • 查找类MRO链上的非数据描述符和普通类属性
    如果实例字典中没有找到目标属性,再次沿着MRO链遍历所有类的__dict__:
    • 如果找到的是非数据描述符(仅实现了__get__方法,没有实现__set__/__delete__,普通实例方法、类方法、静态方法都属于这类),调用其__get__方法返回绑定后的方法对象,终止查找
    • 如果找到的是普通类属性(不是描述符对象),直接返回属性值,终止查找
  • 触发__getattr__兜底逻辑
    如果以上三步都没有找到目标属性,或者查找过程中任意一步抛出了AttributeError异常,Python会检查当前类是否定义了__getattr__方法:
    • 如果定义了,调用__getattr__方法并传入属性名,返回该方法的执行结果
    • 如果没有定义,直接抛出AttributeError异常

可以用两段简单代码验证优先级规则:

# 数据描述符优先级高于实例字典
class DataDesc:
    def __get__(self, instance, owner):
        return "数据描述符返回值"
    def __set__(self, instance, value):
        pass

class Test1:
    attr = DataDesc()
t1 = Test1()
t1.__dict__["attr"] = "实例字典自定义值"
print(t1.attr) # 输出:数据描述符返回值
# 非数据描述符优先级低于实例字典
class NonDataDesc:
    def __get__(self, instance, owner):
        return "非数据描述符返回值"

class Test2:
    attr = NonDataDesc()
t2 = Test2()
print(t2.attr) # 输出:非数据描述符返回值
t2.__dict__["attr"] = "实例字典自定义值"
print(t2.attr) # 输出:实例字典自定义值

类属性访问(Class.attr形式)的特殊逻辑

类本身也是元类(默认是type)实例化出来的对象,因此它的属性查找逻辑和实例属性逻辑完全一致,只是查找层级上移了一层:

  1. 首先调用元类的__getattribute__方法
  2. 先查找元类MRO链上的数据描述符
  3. 再查找类自身的__dict__
  4. 再查找元类MRO链上的非数据描述符和普通元类属性
  5. 都没找到或抛出AttributeError时,调用元类定义的__getattr__(如果存在),否则抛异常

常见认知误区

  • 误区:所有场景都遵循「对象字典优先级高于类字典」。该规则仅在没有匹配数据描述符的前提下成立,数据描述符可以越过实例字典优先生效。
  • 误区:只有属性完全不存在时才会触发__getattr__。只要查找流程中任意环节(包括描述符的__get__方法、property的getter函数)抛出AttributeError,都会触发__getattr__兜底,和属性是否存在无关。
  • 误区:非数据描述符和普通类属性优先级不同。二者在查找时属于同一层级,谁在MRO链上更靠前就返回谁,非数据描述符只是会被额外调用__get__做绑定处理,优先级和普通类属性没有区别。

内容的提问来源于stack exchange,提问作者Matvey_coder3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 19:45:58