理解Python中__dict__是怎样一种体验

Python这门编程语言的最大的好处就是其语言的动态性带来编程的便利性,不像静态语言那样死板,比如它可以运行期动态添加类定义时没有的属性,例如:

class A:
    def __init__(self):
        self.name = 'zhang'
    def eat(self):
        print('eat')
a = A()
a.age = 10
print(a.age)

在Java中这段代码可定是没法通过编译的,但在Python中这样的操作不再是司空见惯的事情,那Python解释器在背后究竟做了什么操作呢?答案是__dict__,Python中的所有内建(build-in)数据类型(type)都有属性__dcit__,当然自定义的类(class)也有,因为自定义的类也是一种数据类型嘛。在a.age=10前后分别加上一行print a.__dict__可以得到结果:

{'name': 'zhang'}
{'age': 10, 'name': 'zhang'}
10

不难看出,在运行期和类定义期所定义的实例对象属性放在该对象的 __dict__属性中,接下来在a=A()后面加上几行代码:

A.country = "China"
print a.country
print a.__dict__

得到结果:

China
{'age': 10, 'name': 'zhang'}

对象a的__dict__属性中并没'country'这个属性,那它是哪来的呢?那么不妨打印类对象A的__dict__看看:

print a.country
print a.__dict__
print A.__dict__

输出结果:

China
{'age': 10, 'name': 'zhang'}
{'country': 'China', '__module__': '__main__', 'eat': <function eat at 0x104e3b230>, ...省略}

这下应该明白了,在运行期间,定义的实例对象(a)的属性是放在a.__dict__中,而定义的类对象(A)的属性是放在A.__dict__中,那为什么实例对象a是怎么访问到country属性的呢? Python中是这样约定的,以obj.attr访问时,它会按照如下顺序去查找:

  1. 对象自身,obj.__dict__['attr']

  2. 对象类型,obj.__class__.__dict__['attr']

  3. 对象类型的基类,obj.__class__.__bases__中的所有__dict__['attr']。注意,当多重继承的情况下有菱形继承的时候,Python会根据MRO确定的顺序进行搜索。

如果在基类都还没找到要访问的属性时,Python解释器就会跑出一个异常:AttributeError。那么问题来了,我们是否可以给内建(build-in)类型添加一些自定义的属性呢?比如:

list.myExtension = lambda self,x:x * 2
list.myExtension(10)

运行的时候,解释器会报错:TypeError: can't set attributes of built-in/extension type 'list',那能否向list的__dict__属性中添加一个值呢?

list.__dict__['myExtension'] = lambda self, x:x * 2

还是一样,没法运行:TypeError: 'dictproxy' object does not support item assignment,细细思来,后者不能运行也是符合情理的,两者就是等价的东西,那为什么不允许程序员扩展内建类型呢?

后面那位睡觉的同学你来回答一下,答曰:Guido van Rossum老爹是用C语言来实现的这些类型,你是没法再修改的,那如何优雅地扩展已有类型呢?知道OOP的同学都会用继承的方式来做。于是那位同学继续睡觉去了。

点击阅读原文了解“__slots__”是什么?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值