getattr & descriptor

Dict

每个实例和类都有相应的__dict__属性,这个属性是一个dictonary。

对于实例,存放实例相关的实例属性等。

对于类,存放类定义中的类属性,函数对象等。

 

new style class指的是继承自object的类,而old style class则不继承自object. 这两种类python的处理方式有所不同。

我们先来看A.__dict__和B.__dict__的区别。

我们可以通过dir函数来获取类和实例的属性,dir将会提供更加详细的信息。对于实例,不仅给出实例的属性,而且还给出类的属性(包括类的成员属性,成员函数定义)。

另外一个函数vars,只给出实例的属性。

 

Member Attribute

The time before descriptor

当我们经由构造函数__init__来对一个成员进行初始化的时候,python会把一个成员变量存放在__dict__中。我们可以由如下代码验证

经过a.foo赋值后,__dict__中多出了一项,这个就是我们通过直接添加成员变量所致。

NOTE:

python的class是对外部开放的,没有必要直接在__init__中初始化成员。这就是动态语言的好处。

What is descriptor

在现在的python中,函数,成员变量等都是用descriptor来实现的。

descriptor分为

  1. 数据描述符
  2. 非数据描述符

数据描述符的定义是拥有__set__、__get__接口的类,而非数据描述符是指只实现了__get__方法。

另外一个可选实现的接口是__delete__。

descriptor可以用来封装对一个属性的访问。

我们来看一个加入descriptor对于一个实例的影响。

以下是descriptor的定义,descriptor是类的静态属性

来看一下访问代码

可以看到,加入了descriptor后,python会先调用我们的set, get函数,最后再对属性做处理。这样我们就有了更多的控制权。__dict__在赋值后也没有改变。

如果我们将descriptor a改为一个非数据描述符,并且对其进行赋值会怎么样呢?

这里我们直接通过del将__set__函数删除,使其成为一个非数据描述符,也可以使用delattr来删除,这个调用是等价的。

在成为非数据描述符后,我们给f.a = 1赋值成功,此时直接在__dict__加入了一项。

这里,我们给出一段python的伪代码,用来描述整个过程:

对于读取一个属性,将会有所不同,如果一个属性是非数据描述符,那么将会先从__dict__中获取,如果没有找到,则再去调用非数据描述符的__get__函数。

测试代码如下:

当我们在f.__dict__中加入了a属性后,python就直接从__dict__中获取该值了。

 

读取一个属性的伪代码如下:

 1 # when read an attribute from a class instance
 2 
 3 # check if there's a data descriptor named 'attribute'
 4 if hasattr(MyClass, 'attribute'):
 5     klsAttr = MyClass.attribute.__class__
 6     if hasattr(klsAttr, '__set__'):
 7         if hasattr(klsAttr, '__get__'):
 8             return klsAttr.__get__(attribute, instance, value)
 9         else:
10             return attribute
11 
12     # we check against the __dict__ of the instance
13     if hasattr(obj, 'attribute'):
14         return obj.__dict__['attribute']
15     
16     if hasattr(klsAttr, '__get__'):
17         return klsAttr.__get__(attribute, instance, value)
18 
19     raise AttributeError('cannot find "attribute"')

 

Descriptor Precedence

这里我们给出不同的descriptor和其他一些重要的属性之间的优先级关系。

数据描述符 > 实例属性 > 非数据描述符

 

Descriptor是类属性,也就是说,每个类只有这样一个Descriptor,所以Descriptor适合用来做类级别的事。比如可以用来做为一个Cache。

 

下次我们要讲Property,而Property是作为实例的Descriptor使用的。

 

reference:

  1. 《Expert python programming》
  2. 《Core python programming》

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值