享学课堂-深入理解 Python 的属性查找

今天我们了解下python的属性查找,在Python中,属性查找(attribute lookup)是比较复杂的,特别是涉及到描述符descriptor的时候。 首先,我们知道: python中一切都是对象,“everything is object”,包括类,类的实例,数字,模块 任何object都是类(class or type)的实例(instance) 如果一个descriptor只实现了get方法,我们称之为non-data descriptor, 如果同时实现了get

__set__

我们称之为data descriptor。

实例属性查找

按照python doc,如果obj是某个类的实例,那么obj.name(以及等价的getattr(obj,’name’))首先调用getattribute。如果类定义了getattr方法,那么在getattribute抛出 AttributeError 的时候就会调用到getattr,而对于描述符(

__get__

)的调用,则是发生在getattribute内部的。官网文档是这么描述的

obj = Clz(), 那么obj.attr 顺序如下:

(1)如果“attr”是出现在Clz或其基类的dict中, 且attr是data descriptor, 那么调用其get方法, 否则

(2)如果“attr”出现在obj的dict中, 那么直接返回 obj.dict[‘attr’], 否则

(3)如果“attr”出现在Clz或其基类的dict中

(3.1)如果attr是non-data descriptor,那么调用其get方法, 否则

(3.2)返回 dict[‘attr’]

(4)如果Clz有getattr方法,调用getattr方法,否则

(5)抛出AttributeError

下面是测试代码:

调用change_attr方法之后,dd_base既出现在类的dict(作为data descriptor), 也出现在实例的dict, 因为attribute lookup的循序,所以优先返回的还是Clz.

__dict__

[‘dd_base’]。而ndd_base虽然出现在类的dict, 但是因为是nondata descriptor,所以优先返回obj.

__dict__

[‘dd_base’]。其他:line48,line56表明了getattr的作用。line49表明obj.

__dict__优先于Clz.__dict__

cached_property例子

我们再来看看上一文章的这段代码

Widget是一个之定义了一个func函数的类,func是类的属性,这个也可以通过Widget.dict、w.dict看到。Widget.dict[‘func’]返回的是一个function,但Widget.func是一个unbound method,即Widget.func并不等同于Widget.dict[‘func’],按照前面的类属性的访问顺序,我们可以怀疑,func是一个descriptor,这样才不会走到第2.2这种情况。验证如下:

可以看到,即使Widget的实例也有一个‘a’属性,但是调用w.a的时候会调用类属性‘a’(一个descriptor)的set方法。如果不注释掉第18到第20行,输出如下

可以看到,优先调用Widget 的setattr方法。因此:对于属性赋值,obj = Clz(), 那么obj.attr = var,按照这样的顺序:

如果Clz定义了setattr方法,那么调用该方法,否则 如果“attr”是出现在Clz或其基类的dict中, 且attr是data descriptor, 那么调用其set方法, 否则 等价调用obj.dict[‘attr’] = var

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20171219A0FH5300?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券