python属性查找顺序

本文探讨Python中变量的作用域顺序,包括局部、嵌套、全局和内建作用域,并详细阐述对象属性查找顺序,讲解`__getattribute__`、`__getattr__`和`__setattr__`在属性访问和赋值中的角色。同时,介绍类的MRO(Method Resolution Order)即继承属性查找顺序,以及描述器如何影响属性访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

以前对这一部分的理解比较混乱,查找了一些资料,自我整理一下。

作用域

对于变量的搜索是按照一定顺序进行的,同名变量将会存在互相屏蔽的问题,所以需要弄清楚python里的作用域信息。在python中作用域一共有四个,按照顺序被搜索:
1. (Local)局部作用域,每当调用一个函数的时候就创建了一个局部作用域,它最先被搜索。
2. (Enclosing)嵌套的父级函数的局部作用域
3. (global)全局作用域
4. (built-in)内建作用域,这个是内建函数和类的作用域。
变量的搜索是由内而外一层层进行的,所以内层的变量会屏蔽外层的变量。但是可以通过使用global和nonlocal关键字,显式的声明一个全局变量或者非局部变量。

对象属性查找顺序

  1. 正常情况下,python中类的属性保存在实例的__dict__属性中,其中__dict__是一个字典,foo.bar等价于
    foo.__dict__['bar'] 同时对于实例的属性的赋值,例如foo.bar = 'yes'等价于foo.__dict__['bar'] = 'yes'
  2. 但是在访问时不会直接进行访问,会调用一个叫做__getattribute__的函数,对于属性进行获取;通过__setattribute__
    对于属性进行修改;但是这些都仅限于__dict__,如果找不到对应属性,则抛出AttributeError
  3. 这个时候就会接着调用一个叫做__getattr__的函数(如果你定义了这个函数),如果这样都找不到就会真的抛出
    AttributeError,对于__setattr__也是类似的。
  4. 而我们通常使用的两个内置函数getattr()和setattr()实际上也是通过这样一个调用过程获取对象的属性。
  5. 而python 对象还有一个跟__getattribute__,__getattr__十分类似的一个属性__get__,但是它们的作用却不太相同,
    所有定义了__get__属性的类都会被转换为一个描述器,具体描述器这里就不展开了,但是有一点就是,在获取对象属性时,
    如果属性有__get__方法,那么就会自动展开__get__方法。

继承属性查找顺序(MRO)

MRO的全称是Method Resolution Order,即方法解析顺序,它用来定义类继承链中如何对类属性和方法进行查找,保证属性查找不会  
出现冲突。现在实际采用的是C3算法,下面简单的描述一下:
我们把类C的线性化(MRO)记为L[C]=[C1, C2,…,CN]。其中C称为L[C]的头,其余元素[C2,…,CN]称为尾。如果一个类C继承自基类  
B1、B2、……、BN,那么可以根据以下两步计算出L[C]:
1. L[object] = [object]
2. L[C(B1…BN)] = [C] + merge(L[B1]…L[BN], [B1]…[BN])
而其中merge函数定义如下:
1. 检查第一个列表的头元素(如 L[B1] 的头),记作 H。
2. 若 H 未出现在其它列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤1;否则,取出下一个列表的头部  
   记作 H,继续该步骤。
3. 重复上述步骤,直至列表为空或者不能再找出可以输出的元素。如果是前一种情况,则算法结束;如果是后一种情况,说  
   明无  法构建继承关系,Python 会抛出异常。
详细的例子可以看这里 [C3算法](https://en.wikipedia.org/wiki/C3_linearization)
在实际使用中,可以通过类的`__mro__`属性和`mro`方法获得类的属性查找顺序,返回结果类似(C, A, B, object),
而与此相关有一个十分常用的函数super,其形式为super(type, [object or type]),实际上super函数不能简单的看作获得父 
类对象,实际上它是在mro的基础列表的基础上获得下一个基类对象。第二个参数提供mro列表,第一个参数定位当前位置,  
并返回列表中的下一个类对象,类似于下面的实现:
def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls)+1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值