非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感
关键字:名称,名称空间,引用,指针,指针类型的指针(即指向指针的指针)
我读完后的理解总结:
1. 我们知道,python中的变量的赋值操作,变量其实就是一个名称name,赋值就是将name引用到一个object对象。name就可以看作是指向object的指针。
2. 有了name看作指针的概念。当一个类A定义时,定义了一个类属性名字叫class_attr01。
代码如下:
class A(object):
class_attr01 = 666 def test(self):
pass a_obj = A()
print('查看a_obj名称空间中的名字',dir(a_obj)) print('通过a_obj访问A类的class_attr01', a_obj.class_attr01, id(a_obj.class_attr01))
print('校验A.class_attr01和a_obj.class_attr01是否相同', A.class_attr01 is a_obj.class_attr01) # 第一种情况改变a_obj.class_atrr01:
a_obj.class_attr01 = 777
print('改变a_obj.class_attr01后:', a_obj.class_attr01, A.class_attr01)
# 第二种情况改变A.class_attr01: 执行第二种情况代码时,要注释掉第一种情况代码,保持两种情况执行的前提条件是一样的。
A.class_attr01 = 777
print('改变A.class_atrr01后:', a_obj.class_attr01, A.class_attr01) ------------------------------------------------------------ 第一种情况打印结果--------------------------
查看a_obj名称空间中的名字 ['test', 'class_attr01', ....]
通过a_obj访问A类的class_attr01 666 2445972197136
校验A.class_attr01和a_obj.class_attr01是否相同 True
改变a_obj.class_attr01后: 777 666
----------------------------------------------------------- 第二种情况打印结果------------------------
查看a_obj名称空间中的名字 ['test', 'class_attr01',....]
通过a_obj访问A类的class_attr01 666 2732605729392
校验A.class_attr01和a_obj.class_attr01是否相同 True
改变A.class_atrr01后: 777 777
------------------------------------------------------
第一行打印结果分析:
这里A类没有定义__init__初始化对象方法。当实例化一个A对象a_obj后,a_obj中应该是没有任何名称或者说属性的。但是从第一行打印结果中我们可以看到
a_obj名称空间中是有一个class_attr01名称的。得出第一个结论就是:(在没有通过__init__初始化方法创建名称或者对象.方式创建的前提下)类实例化对象时,对象名称空间中也会创建类属性同名的名称,其实可以说
类名称空间中的名称都会在对象名称空间创建相同的名称。至于名称指向的什么,客官往下面看: 第二行和第三行打印结果分析:
再看看,这个名称引用的对象是什么呢?从打印结果看就是A中属性class_attr01引用的对象666。
这时我们就有一个假象:
a_obj.class_attr01 和 A.class_attr01是一样的。通过id(a_obj.class_attr01) 确实 id(A.class_attr01) 是相同的。我们可能得出这样一个结论:
a_obj.class_attr01名称指向 666 和A.class_attr01名称指向的是同一对象666。也就是说两个名称指向了同一个对象666. 有此结论,那是不是我们可以通过赋值语句改变其中一个指向而不会影响另一个呢? 第一种情况的第四行打印结果分析(注释掉第二种情况代码):
通过改变a_obj.class_attr01 = 777 赋值后,我们看到确实改变a_obj.class_attr01没有改变A.class_attr01指向的666。 下面我们反过来,把代码的a_obj.class_attr01 = 777 替换为 A.class_atrr01 = 777
第二种情况的第四行打印结果分析(将第二种情况复原,并注释第二种情况代码)
违背了我们刚才得到的结论(有此结论,那是不是我们可以通过赋值语句改变其中一个指向而不会影响另一个呢?) 。
因为a_obj.class_attr01 随同 A.class_attr01 都变为 777了。惊讶:a_obj.class_atrr01 赋值改变不了 A.class_atrr01 反过来则可以。不是说,两个名称指向同一对象,赋值一个应该不影响另一个呀。
那只能说明a_obj.class_atrr01 和 A. class_atrr01 还是有区别的。这就是要提到的 “指向指针的指针”。
解释刚才的现象: a_obj.class_atrr01 在实例化对象时,是指向A.class_atrr01这个名称的(指针),即a_obj.class_attr01是一个指针,A.class_atrr01也是一个指针;
而a_obj在实例化时a_obj.class_atrr01这个指针是指向A.class_atrr01这个指针的(指向指针的指针);A.class_atrr01这个指针是指向666这个整数对象的。
所以,a_obj.class_atrr01解析引用对象时也是引用到了666。但是在赋值777后a_obj.class_atrr01指向了777对象,而不是指针了,所以解析后就编程777,而A.class_atrr01没有变,还是指向666.
反过来,实例化a_obj后,只对A.class_atrr01进行重新赋值777,那么A.class_atrr01就指向777即引用到777对象。这时的a_obj.class_atrr01是一个指向指针的指针,即指针还是指向A.class_atrr01,所以
a_obj.class_atrr01通过指针A.class_atrr01 引用到了相同的对象777.
这样就能解释通,上面违背现象的原因了。 有一种特殊情况,就是对于mutable可变对象,可以通过指针的指针改变其值,类也发生变化。 总结: 使用对象和其类相同名称的属性时,要考虑当前属性名称指向的是哪里?有没有被赋值过而改变了其指向。 不知道我表述清楚没有,不过通过下面博客内容的实验,也可以验证我的说法,只不过作者论证没有提及指针,和指针类型的指针来解释,不过结论是相似的,即: 类属性在同类及其子类之间互相影响」必须有一个前提条件:实例建立后,其类属性从来没有被重新赋值过,即类属性依然指向最初所指向的内存地址
----------------------分隔线----------------以下是阅读的博客文章内容(转)-----------------------
Python 中的引用和类属性的初步理解
最近对Python 的对象引用机制稍微研究了一下,留下笔记,以供查阅。
首先有一点是明确的:「Python 中一切皆对象」。
那么,这到底意味着什么呢?
如下代码:

#!/usr/bin/env python a = [0, 1, 2] # 来个简单的list # 最初,list 和其中各个元素的id 是这样的。
print 'origin'
print id(a),a
for x in a:
print id(x), x
print '----------------------' # 我们把第一个元素改改
print 'after change a[0]'
a[0] = 4
print id(a),a
for x in a:
print id(x), x
print '----------------------' # 我们再把第二个元素改改
print 'after change a[1]'
a[1] = 5
print id(a),a
for x in a:
print id(x), x
print '----------------------' # 回头看看直接写个0 ,id是多少
print 'how about const 0?'
print id(0), 0

运行结果如下:

PastgiftMacbookPro:python pastgift$ ./refTest.py
Origin
4299760200 [0, 1, 2]
4298181328 0
4298181304 1
4298181280 2
----------------------
after change a[0]
4299760200 [4, 1, 2]
4298181232 4
4298181304 1
4298181280 2
----------------------
after change a[1]
4299760200 [4, 5, 2]
4298181232 4
4298181208 5
4298181280 2
----------------------
how about const 0?
4298181328 0

从「Origin」部分来看,list 中各个元素的地址之间都正好相差24,依次指向各自的数据——这让我想到了数组。
当修改a[0] 的值之后,发现,a[0] 的地址发生了变化。也就是说,赋值语句实际上只是让a[0] 重新指向另一个对象而已。此外,还注意到,a[0] 的地址和a[2]的地址相差48(2个24)。
当再次修改a[1] 之后,同样地,a[1] 的地址也发生变化,有趣的是,这次a[1] 的地址和a[0] 的地址又相差24,和原先的a[2] 相差72(3个24)。
最后,当直接把数字0的地址打印出来后,发现它的地址和最开始的a[0] 的地址完全一样。
至此,基本可以说明,就算是list 中的元素,其实也是引用。修改list 中的元素,实际上还是在修改引用而已。
对于Python 中类属性,有人提到过「类属性在同一类及其子类之间共享,修改类属性会影响到同一类及其子类的所有对象」。
这里提到的:http://www.cnblogs.com/vamei/archive/2012/06/02/2532018.html
听着挺吓人,但仔细研究之后,其实倒也不是什么大不了的事情。
如下代码:

#!/usr/bin/env python class Bird(object):
name = 'bird'
talent = ['fly'] class Chicken(Bird):
pass bird = Bird();
bird2 = Bird(); # 同类实例
chicken = Chicken(); # 子类实例 # 最开始是这样的
print 'Original attr'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------' # 换个名字看看
bird.name = 'bird name changed!' print 'after changing name'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------' # 洗个天赋试试(修改类属性中的元素)
bird.talent[0] = 'walk' print 'after changing talent(a list)'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------' # 换个新天赋树(整个类属性全换掉)
bird.talent = ['swim'] print 'after reassign talent'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------' # 洗掉新天赋树(对新来的类属性中的元素进行修改)
bird.talent[0] = 'dance' print 'changing element after reassigning talent'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'

运行结果:

PastgiftMacbookPro:python pastgift$ ./changeAttributeTest.py
Original attr
4301998000 bird
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
----------------------------
after changing name
4301986984 bird name changed!
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
----------------------------
after changing talent(a list)
4301986984 bird name changed!
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
----------------------------
after reassign talent
4301986984 bird name changed!
4301859512 ['swim']
4301998000 bird
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
----------------------------
changing element after reassigning talent
4301986984 bird name changed!
4301859512 ['dance']
4301998000 bird
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
----------------------------

在「Origin」的时候,同类对象,子类对象的相同类属性的地址都是相同的——这就是所谓的「共享」。
修改name 之后,只有被修改的对象name 属性发生变化。这是因为对name的赋值操作实际上就是换了一个字符串,重新引用。字符串本身并没有发生变化。所以并没有在同类和子类之间产生互相影响。
接下来,修改talent 中的元素。这时,情况有所改变:同类及其子类的talent 属性都一起跟着变了——这很好理解,因为它们都引用的内存地址都一样,引用的是同一个对象。
再接下来,给talent 重新赋值,也就是改成引用另外一个对象。结果是只有本实例的talent 属性变化了。从内存地址可以看出,本实例和其他实例的talent 属性已经不再指向相同的对象了。就是说「至此,本实例已经是圈外人士了」。
那么,最后再次修改talent 中元素后,对其他实例无影响的结果也是很好理解了。因为已经是「圈外人士」了嘛,我再怎么折腾也都是自己的事情了。
所以,「类属性在同类及其子类之间互相影响」必须有一个前提条件:实例建立后,其类属性从来没有被重新赋值过,即类属性依然指向最初所指向的内存地址。
最后提一下对象属性
如下代码:

#!/usr/bin/env python class Bird(object):
def __init__(self):
self.talent = ['fly'] bird = Bird()
bird2 = Bird() # 刚开始的情形
print 'Origin'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------' # 修改其中一个对象的属性
bird.talent[0] = 'walk' print 'after changing attribute'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------' # 作死:两个对象的属性指向同一个内存地址,再修改
bird.talent = bird2.talent
bird.talent[0] = 'swim' print 'assign to another attribute and change it'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'

运行结果:

PastgiftMacbookPro:python pastgift$ ./changeAttributeTest2.py
Origin
4299867632 ['fly']
4299760200 ['fly']
--------------------
after changing attribute
4299867632 ['walk']
4299760200 ['fly']
--------------------
assign to another attribute and change it
4299760200 ['swim']
4299760200 ['swim']
--------------------

由于对象属性就算内容完全一样(刚初始化后的属性内容一般都是一样的),也会分配到完全不同的内存地址上去。所以不存在「同类对象之间影响」的情况。
但如果让一个对象的属性和另一个对象的属性指向同一个地址,两者之间(但也仅限两者之间)便又互相牵连起来。
非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感的更多相关文章
- 【iOS开发-72】设置状态栏的两种方式、程序生命周期以及更好地理解几大类(对象)之间的关系
(1)设置状态栏的2种方式 --第一种方式就是我们在控制器中设置,系统默认就是交给视图控制器去管理的,这样不同视图控制器能够自己定义不同的状态栏例如以下: -(BOOL)prefersStatusBa ...
- Qt 对象间的父子关系
C++中只要有一个new就必须要有一个delete与之对应 但是Qt中的对象之间有特殊的关系 Qt 对象间的父子关系 每一个对象都保存有它所有子对象的指针 每一个对象都有一个指向其父对象的指针 par ...
- 7.QT-Qt对象间的父子关系
Qt对象之间可以存在父子关系 继承于QObject类或者其子类的对象,都称为Qt对象 当指定Qt对象的父对象时 需要通过setParent()成员函数来设置对象间的父子关系 子对象将会把自己的指针地址 ...
- 让两个对象间建立weak关系
让两个对象间建立weak关系 这是为了给两个对象间建立weak关系,当一个对象被释放时,另外一个对象再获取这个值时就是nil,也就是不持有这个对象:) 源码: WeakRelatedDictionar ...
- python中如何统计一个类的实例化对象
类中的静态变量 需要通过类名.静态变量名 来修改 :通过对象不能修改 python中如何统计一个类的实例化对象?? class Person: #静态变量count,用于记录类被实例化的次数 coun ...
- python中的引用传递,可变对象,不可变对象,list注意点
python中的引用传递 首先必须理解的是,python中一切的传递都是引用(地址),无论是赋值还是函数调用,不存在值传递. 可变对象和不可变对象 python变量保存的是对象的引用,这个引用指向堆内 ...
- python的str,unicode对象的encode和decode方法, Python中字符编码的总结和对比bytes和str
python_2.x_unicode_to_str.py a = u"中文字符"; a.encode("GBK"); #打印: '\xd6\xd0\xce\xc ...
- Python 中的引用和类属性的初步理解
最近对Python 的对象引用机制稍微研究了一下,留下笔记,以供查阅. 首先有一点是明确的:「Python 中一切皆对象」. 那么,这到底意味着什么呢? 如下代码: #!/usr/bin/env py ...
- python学习之【第十七篇】:Python中的面向对象(类和对象)
1.什么是类和类的对象? 类是一种数据结构,我们可以用它来定义对象,后者把数据值和行为特性融合在一起,类是现实世界的抽象的实体以编程形式出现.实例是这些对象的具体化.类是用来描述一类事物,类的对象指的 ...
随机推荐
- SpringCloud分布式微服务搭建(一)
本例子主要使用了eureka集群作为注册中心来保证高可用,客户端来做ribbon服务提供者的负载均衡. 负载均衡有两种,第一种是nginx,F5这种集中式的LB,对所有的访问按照某种策略分发. 第二种 ...
- 部署Chart应用并使用.net core读取Kubernetes中的configMap
上一篇文章讲了 k8s使用helm打包chart并上传到腾讯云TencentHub,今天就讲一下使用Helm部署应用并使用configMap代替asp.net core 中的appsettings.j ...
- Error Code: 1044. Access denied for user 'root'@'%' to database
mysql> SELECT host,user,password,Grant_priv,Super_priv FROM mysql.user; +--------------+--------- ...
- HTTP 视频怎么在 MIP 页面中使用?
在 MIP 中,一些资源的使用需要支持 HTTPS,视频就是其中一种.但目前大部分站点的视频资源都还是 HTTP 的资源,无法在百度 MIP 搜索结果中直接使用, mip-video 视频组件针对 H ...
- Promise (1) 如何使用Promise
Promise 也是面试高频问题, 今天我们来看看Promise是什么, 能做什么, 怎么用, 下一期我们自己来模拟一个myPromise 1 Promise 是什么 我们要学会自己给自己提问, 才 ...
- 为什么面试你要25K,HR只给你20K?
周末了,我们来聊个轻松的话题,关于涨薪,哈哈~ 前阵子,栈长给大家分享了<为什么公司宁愿 25K 重新招人,也不给你加到 20K?>,今天我们来聊一个差不多的话题: 为什么面试你要25K, ...
- 浅谈unity中gamma空间和线性空间
转载请标明出处:http://www.cnblogs.com/zblade/ 一.概述 很久没有写文章了,今天写一篇对gamma空间和线性空间的个人理解总结,在查阅和学习了各个资料后,算是一个个人笔记 ...
- Properties类对于文件的读取和写入
Properties类表示一个持久的属性集.Properties可保存在流中或从流中加载.Properties对象只能加载以 .Properties 为后缀的文件(文件我创建在src下). 开始时文件 ...
- WeihanLi.Redis自定义序列化及压缩方式
WeihanLi.Redis自定义序列化及压缩方式 Intro WeihanLi.Redis 是基于 StackExchange.Redis 的扩展,提供了一些常用的业务组件和对泛型的更好支持,默认使 ...
- 重庆3Shape TRIOS都有哪些功能
1.高质量的临床结果高准确度的数字化印模确保高质量的修复体密合度即时制备和印模确认就位修复体时无需多次调整和研磨2.轻松取模无需喷粉扫描无需印模材料,可保持操作台整洁无需重复取模3.更稳健的业务联系加 ...