马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 heidern0612 于 2018-12-16 19:56 编辑
写心得的过程都是自我思考和探索的过程,借鉴和摘抄了了很多论坛前辈和互联网大佬的经验,仓促惶恐间难免错误百出,如有错误,恳请指正,不胜感激。
原贴地址:戳我前进
名字有点奇怪,最近论坛中出现了很多询问类的问题,看来大家跟我一样对类属性和实例属性不太了解。
借这个题目,跟大家一起共同讨论下,为了方便,我还是先举个栗子:
class A():
b = 1
#情形1
obj1 = A()
obj2 = A()
print(obj1.b,obj2.b,A.b)
#情形2
obj1.b += 2
print(obj1.b,obj2.b,A.b)
#情形3
A.b += 3
print(obj1.b,obj2.b,A.b)
情形1的结果为:1 1 1
情形2的结果为:3 1 1
情形3的结果为:3 4 4
首先为什么会有这个问题呢?
因为b属性被称为类属性,既然是类属性,那么根据经验和佳宇老师的讲课来进行判断,类属性应该是为其实例所共享的。
很自然的,既然是共享关系,那么从类的层次改变b的值,自然其实例的b的值也要跟着变化了。
可是情形3的情况却说明,上面的说法是错的,错哪里呢?这就要从Python的类属性讲起。
①、Python中属性的获取:
对于属性,我们通常采用类.属性或实例.属性的形式调用。
例如上例中的A.b属于类.属性形式,obj1.b属于实例.属性的形式。
②、Python中属性的设置:
对于属性的设置我们通常采用类.属性 = 值或实例.属性 = 值的形式
例如obj1.b = 3
上例中obj1.b += 2等价于obj1.b = obj1.b + 2,这句话包含了属性获取及属性设置两个操作。
OK,重点来了,Python中属性的获取和设置的机制与静态语言是不同的,正是背后机制的不同,导致了Python中类属性不一定是为其实例所共享的。
③、Python中属性查找机制:
Python中属性的获取存在一个向上查找机制,还是拿上面的例子做说明:
Python中一切皆对象,A属于类对象,obj1属于实例对象,从对象的角度来看,A与obj1是两个无关的对象,但是,Python通过下面的查找树建立了类对象A与实例对象obj1、obj2之间的关系。
A | ------------- | | | | obj1 obj2
当调用A.b时,直接从A获取其属性b。
但是情形1中调用obj1.b时,Python按照从obj1到A的顺序由下到上查找属性b。
值得注意的这时候obj1是没有属性b的,于是,Python到类A中去查找,成功找到,并显示出来。
所以,从现象上来看,A的属性b确实是共享给其所有实例的,虽然这里只是从查找树的形式模拟了其关系。
④、Python中的属性设置
问题的关键在于情形2中obj1.b += 2,为什么呢?
上面我们指出obj.b += 2包含了属性获取及属性设置两个操作。
即obj1.b += 2等价于obj1.b = obj1.b + 2。
其中等式右侧的obj.b属于属性获取,其规则是按照上面提到的查找规则进行,即,这时候,获取到的是A的属性b,所以等式左侧的值为12。
第二个操作是属性设置,即obj.b = 12。当发生属性设置的时候,obj1这个实例对象没有属性b,因此会为自身动态添加一个属性b。
由于从对象的角度,类对象和实例对象属于两个独立的对象,所以,这个b属性只属于obj1,也就是说,这时候类对象A和实例对象obj1各自有一个属性b。
那么,在情形3中,再次调用obj1.b时,按照属性调用查找规则,这个时候获取到的是实例对象obj1的属性b,而不是类对象A的属性b。
⑤、对问题探讨的总结:
到这里就可以完满解释上面的问题:
1. Python中属性的获取是按照从下到上的顺序来查找属性;
2. Python中的类和实例是两个完全独立的对象;
3. Python中的属性设置是针对对象本身进行的;
①、从头再看对情形1的解释:
因为Python中的属性获取是按照从下到上的顺序来查找的,所以在情形1:
实例对象obj1和obj2不存在属性b。
证明如下:
>>> obj1.__dict__
{}
>>> obj2.__dict__
{}
所以,此时,obj1.b, obj2.b, A.b实质上都是指A.b。因此,输出同样的结果。
②、对情形2的解释:
因为Python中的类和实例是两个完全独立的对象且Python中的属性设置是针对对象本身进行的,所以在情形2:
实质上是对实例对象obj1设置了属性b,并赋值为3。证明如下: >>>> obj1.b = 3
>>> obj1.__dict__
{'b': 3}
>>> obj2.__dict__
{}
因此,再次调用obj1.b时,将获取到的是实例对象obj1的属性b,而不是类对象A的属性b。而对于实例对象obj2,由于其并没有属性b,所以调用obj2.b时,获取到的仍是A的属性b。
③、对情形3的解释:
顺利理解了前两个情形,那么第3个情形就很容易了,改变A的属性b只能影响到类对象A和实例对象obj2,不能影响obj1,因为,obj1存在b,在获取时,不会获取到A的属性。
写在最后的总结:
如果实例对象没有调用类属性的话,会使用类对象的属性值。
如果实例对象调用类属性的话,会创建一个类对象属性一样的副本给自己,再次调用实例对象的类属性时,这个类属性其实已经不是类对象的类属性,而是类属性的副本了,它只属于实例对象。
为了加深大家对类属性和实例属性的理解,我给一个论坛最近关于类的解答问题:地址1。
(P.S:不要看我的回答,我的回答是不正确的,大家看塔利班大神的回答就可以了。 )
|