非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感...
关键字:名称,名称空间,引用,指针,指针类型的指针(即指向指针的指针) 我读完后的理解总结: 1. 我们知道,python中的变量的赋值操作,变量其实就是一个名称name,赋值就是将name引用到一个object对象。name就可以看作是指向object的指针。 2. 有了name看作指针的概念。当一个类A定义时,定义了一个类属性名字叫class_attr01。 代码如下: class A(object):class_attr01 = 666def test(self):passa_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 pythona = [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 pythonclass Bird(object):name = 'bird'talent = ['fly']class Chicken(Bird):passbird = 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 pythonclass 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'] --------------------
由于对象属性就算内容完全一样(刚初始化后的属性内容一般都是一样的),也会分配到完全不同的内存地址上去。所以不存在「同类对象之间影响」的情况。
但如果让一个对象的属性和另一个对象的属性指向同一个地址,两者之间(但也仅限两者之间)便又互相牵连起来。
转载于:https://www.cnblogs.com/ZJiQi/p/9922046.html
非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感...相关推荐
- python中exception类的_面试题 | 列举几个Python中的标准异常类?
[摘要]今天给大家解答一道Python常见的面试题,希望这个面试栏目,给那些准备面试的同学,提供一点点帮助!小编会从最基础的面试题开始,每天一题.如果参考答案不够好,或者有错误的话,麻烦大家可以在留言 ...
- 7.QT-Qt对象间的父子关系
Qt对象之间可以存在父子关系 继承于QObject类或者其子类的对象,都称为Qt对象 当指定Qt对象的父对象时 需要通过setParent()成员函数来设置对象间的父子关系 子对象将会把自己的指针地址 ...
- 第十六课、Qt对象间的父子关系------------------狄泰软件学院
一.Qt对象间的关系 1.Qt对象间可以存在父子关系 (1).每个对象都保存有它所有子对象的指针 (2).每一个对象都有一个指向其父对象的指针 2.当指定Qt对象的父对象时 (1).其父对象会在子对象 ...
- oracle对象依赖关系图,Oracle concepts 学习笔记(4)——Schema对象间的依赖关系
Schema对象间的依赖关系 一些对象的定义,包括视图和存储过程,需要引用其它的对象,比如表.因此,这些对象的定义将依赖于被引用的对象的定义. 一.简单介绍依赖 一些类型的schema对象可以引用其它 ...
- 【Python面试】 列举Python中的标准异常类?
往期面试题: 说说Python变量.函数.类的命名规则? 说说Python可变与不可变数据类型? 说说Python面向对象三大特性? 说说Python中有几种数据类型? 说说Python模块主要分哪三 ...
- python定义类的程序_python扫码签到程序python中如何定义类
什么是类? 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.对象是类的实例. 什么是方法? 类中的函数即为方法 如何定义一个类? 定义类,语法格式如下: < ...
- python中所有的异常类都是谁的子类_Python中所有的异常类都是____的子类。
[简答题]假设成年人的体重和身高存在此种关系: 身高(厘米)-100 =标准体重(千克) 如果一个人的体重与其标准体重的差值在正负5%之间,显示"体重正常",其他则显示" ...
- python 概率分布函数_如何在Python中实现这五类强大的概率分布
匿名用户 1级 2016-04-25 回答 首页 所有文章 观点与动态 基础知识 系列教程 实践项目 工具与框架应用 工具资源 伯乐在线 > Python - 伯乐在线 > 所有文章 &g ...
- python中的面向对象:类与对象(重点!!!)
Python中重点与难点,必须熟练掌握!!! 一.面向对象 1.面向对象概念 面向对象(Object Oriented,OO)是软件开发方法,是一种思想.面向对象的对象是指客观世界存在的事物. 我们之 ...
最新文章
- 急需降低系统复杂性,我们从 Kafka 迁移到了 Pulsar
- 好书 《古代的中医》 《麦肯锡卓越工作方法》
- flash与IPhone
- python selenium框架_基于python+selenium的框架思路
- 用Windows live Writer 2012发布51cto博客
- scrapy从入门到放弃 学习项目2
- 梯度下降法_梯度下降
- [css] sass是怎么定义变量的?
- python内建函数和工厂函数的整理
- 阿里云服务器Debian11系统安装Linux宝塔面板 搭建WordPress个人博客
- 利用Gtmetrix检测你的网站载入速度!
- 双人游戏根据胜负关系匹配
- 使用html制作一个网页
- 蚂蚁金服实习Android岗,面试闯关记。
- 关于log4j:WARN No appenders could be found for logger (org.apache.hadoop.metrics2.lib.MutableMetricsFa
- Pool:对象池源码解读
- Unity项目--LoyPoly风格的FPS Demo(附试玩地址)
- org.hibernate.TransientObjectException:The given object has a null identifier
- nmap简单实用命令
- 什么是勒索病毒,勒索病毒简介,电脑中病毒了怎么修复
热门文章
- linux下mysql数据库目录迁移_mysql实现linux下数据库目录迁移
- python3.7降级3.6_请问一下Mac python3.7.1怎么降低到3.6版本?
- 风变编程python网址_风变编程:职场学习Python的重要性
- csh sum算总和_如何在R中使用sum()–在R中查找元素的总和
- Android XML解析器– XMLPullParser
- 使用Kotlin的Android Spinner
- linux vmstat_Linux中vmstat命令指南
- 每个人都应该知道的Android Studio快捷方式
- Android MVP和Dagger2
- VSCode调试Python时终端输出中文乱码解决方法2