关键字:名称,名称空间,引用,指针,指针类型的指针(即指向指针的指针)
我读完后的理解总结:
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 中的引用和类属性的初步理解》读后感的更多相关文章

  1. 【iOS开发-72】设置状态栏的两种方式、程序生命周期以及更好地理解几大类(对象)之间的关系

    (1)设置状态栏的2种方式 --第一种方式就是我们在控制器中设置,系统默认就是交给视图控制器去管理的,这样不同视图控制器能够自己定义不同的状态栏例如以下: -(BOOL)prefersStatusBa ...

  2. Qt 对象间的父子关系

    C++中只要有一个new就必须要有一个delete与之对应 但是Qt中的对象之间有特殊的关系 Qt 对象间的父子关系 每一个对象都保存有它所有子对象的指针 每一个对象都有一个指向其父对象的指针 par ...

  3. 7.QT-Qt对象间的父子关系

    Qt对象之间可以存在父子关系 继承于QObject类或者其子类的对象,都称为Qt对象 当指定Qt对象的父对象时 需要通过setParent()成员函数来设置对象间的父子关系 子对象将会把自己的指针地址 ...

  4. 让两个对象间建立weak关系

    让两个对象间建立weak关系 这是为了给两个对象间建立weak关系,当一个对象被释放时,另外一个对象再获取这个值时就是nil,也就是不持有这个对象:) 源码: WeakRelatedDictionar ...

  5. python中如何统计一个类的实例化对象

    类中的静态变量 需要通过类名.静态变量名 来修改 :通过对象不能修改 python中如何统计一个类的实例化对象?? class Person: #静态变量count,用于记录类被实例化的次数 coun ...

  6. python中的引用传递,可变对象,不可变对象,list注意点

    python中的引用传递 首先必须理解的是,python中一切的传递都是引用(地址),无论是赋值还是函数调用,不存在值传递. 可变对象和不可变对象 python变量保存的是对象的引用,这个引用指向堆内 ...

  7. python的str,unicode对象的encode和decode方法, Python中字符编码的总结和对比bytes和str

    python_2.x_unicode_to_str.py a = u"中文字符"; a.encode("GBK"); #打印: '\xd6\xd0\xce\xc ...

  8. Python 中的引用和类属性的初步理解

    最近对Python 的对象引用机制稍微研究了一下,留下笔记,以供查阅. 首先有一点是明确的:「Python 中一切皆对象」. 那么,这到底意味着什么呢? 如下代码: #!/usr/bin/env py ...

  9. python学习之【第十七篇】:Python中的面向对象(类和对象)

    1.什么是类和类的对象? 类是一种数据结构,我们可以用它来定义对象,后者把数据值和行为特性融合在一起,类是现实世界的抽象的实体以编程形式出现.实例是这些对象的具体化.类是用来描述一类事物,类的对象指的 ...

随机推荐

  1. Java注解(一):介绍,作用,思想及优点

    “注解优先于命令模式”-出自<Effective Java> Java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供 ...

  2. Spark学习之数据读取与保存总结(二)

    8.Hadoop输入输出格式 除了 Spark 封装的格式之外,也可以与任何 Hadoop 支持的格式交互.Spark 支持新旧两套Hadoop 文件 API,提供了很大的灵活性. 要使用新版的 Ha ...

  3. 从壹开始微服务 [ DDD ] 之终篇 ║当事件溯源 遇上 粉丝活动

    回首 哈喽~大家好,时间过的真快,关于DDD领域驱动设计的讲解基本就差不多了,本来想着周四再开一篇,感觉没有太多的内容了,剩下的一个就是验证的问题,就和之前的JWT很类似,就不打开一个章节了,而且这个 ...

  4. Kubernetes集群部署史上最详细(一)Kubernetes集群安装

    适用部署结构以及版本 本系列中涉及的部署方式和脚本适用于1.13.x和1.14,而且采取的是二进制程序部署方式. 脚本支持的部署模式 最小部署模式 3台主机,1台为k8s的master角色,其余2台为 ...

  5. java_stream流

    Stream流的个人理解 整体来看,流式思想类似于工厂车间的“生产流水线”,通过一些列操作来获取我们需要的产品 在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念 ...

  6. Spring中的@conditional注解

    今天主要从以下几方面来介绍一下@Conditional注解 @Conditional注解是什么 @Conditional注解怎么使用 1,@Conditional注解是什么 @Conditional注 ...

  7. HTML入门知识汇总

    1. HTML认识 1.1 什么是HTML HTML是描述(制作)网页的语言,指的是超文本标记语言(Hyper Text Markup Language). 超文本:就是指页面内可以包含图片.链接.甚 ...

  8. 需求分析&用例编写

    一.需求分析? 1.什么是需求 软件产品必须完成的是以及必须具备的品质. 功能性需求:产品必须完成的那些事,要求一定的功能和品质. 例子:淘宝的用户名登录. 非功能性需求:产品必须具备的属性和品质.诸 ...

  9. windows下nginx的安装及使用

    安装过程比较简单 1.下载nginx http://nginx.org/en/download.html 下载稳定版本,以nginx/Windows-1.14.2为例,直接下载 nginx-1.14. ...

  10. C/C++中extern和static

    目录 1 extern概念 2 extern作用 2.1 变量声明 2.2 变量定义 2.3 声明和定义举例 3 为什么使用extern 4 怎么使用extern 4.1 基本数据类型定义变量 4.2 ...