Object References, Mutability, and Recycling

本章章节:

  • Variables Are Not Boxes
  • identity , Equality ,  Aliases
  • Copies are shallow by default
  • Function Parameters as references
  • del and Garbage Collection
  • Weak References
  • Tricks Python Plays with Immutable

Variables

翻译过来叫做变量,其实就是指针/标签。指向一个内存地址。而别名就是说同一个对象有多个标签。

identity

完全一样的两个对象,区别它们的是identity,Python使用id()函数得到一个对象的identity。

==符号是object.__eq__的语法糖。它会调用内存中的对象进行比较。

is符号是object.__id__的语法糖,只会比较对象的id,自然运行速度快于==。

Copies are shallow by default

tuple模式是浅层复制。使用list()或者copy()复制的是tuple内的第一层对象。

copy模块,深层复制

可用于深层的复制。

copy.copy(x)返回x层的浅层复制。

Function Parameters as References

call by sharing共享传参。这是Pyhon唯一支持的参数传递模式。Ruby等OO对象语言都是这种模式。

一个函数可以改变传递给它,作为参数的可变对象(multable object)的值,但不能改变对象的identity。

形式参数是别名:

例子:

def f(a, b):
print(id(a), id(b)) #
a += b
print(id(a))
return a x = 1
print("id(x) is %s" % id(x))
y = 2
print("id(y) is %s" % id(y))
f(x, y)
id(x) is 4565326480
id(y) is 4565326512
4565326480 4565326512
4565326544

由此可知,x和a, y和b都是同一个对象的引用。所以说a, b只是别名。

而a += b代码中的a, 的id改变了。这代表它引用的是另一个不可变对象(整数)。

改变可变对象的值

对上面的例子进行修改:

def f(a, b):
print(id(a), id(b)) #
a += bprint("change variable a, but its id not change: {}".format(id(a)))
return a x = [1, 2]
print("id(x) is %s" % id(x))
y = [2, 3]
print("id(y) is %s" % id(y))
print("return is {}".format(f(x, y)))
print("x is {}".format(x))

x是一个list,属于可变对象。

id(x) is 4545995264
id(y) is 4545996928
4545995264 4545996928
change variable a, but its id not change: 4545995264
return is [1, 2, 2, 3]
x is [1, 2, 2, 3]

由此验证了一个函数可以改变传递给它,作为参数的可变对象(multable object)的值,但不能改变对象的identity。

如果把x,y改成一个tuple,那么函数不能对x进行修改了。

call by share原理,不要把可变对象作为默认的参数。

这个原理体现了一些方便。但是:

class HauntedBus:
def __init__(self, passengers = []):
self.passengers = passengers def pick(self, name):
self.passengers.append(name)
bus1 = HauntedBus()
bus1.passengers.append("Tom")
print("bus1 has {}".format(bus1.passengers))
bus2 = HauntedBus()
print("bus2 has {}".format(bus2.passengers))

结果竟然是:

bus1 has ['Tom']
bus2 has ['Tom']

bus2也有了一个乘客Tom!

原因就是因为它们共享了一个参数passengers,它是一个list类型,是可变参数。由于共享,所以出现了两个对象数据混淆的问题。

背后的过程是:passengers = []是在模块加载时,定义函数的过程中被计算。

避免也简单,如果需要传入数据,在函数内进行复制,使用复制的数据。


del and Garbage Collection

Object不会被自行销毁,但是当认为是垃圾时,就会被当作垃圾回收。

del命令删除的是name, 而不是对象本身。(name就是指向对象的指针)。

当一个对象,没有指针来引用它,那么就被当成是垃圾,最后被销毁。

垃圾回收的算法机制:reference counting

每个对象有一个计数,记录对它的引用(指针)有多少个,当为0个时,对象就会被销毁。

CPython会调用__del__方法,对象被销毁,释放内存。

当然Python除了reference counting这种回收机制,还有更复杂的垃圾回收机制。

例子:

>>> import weakref
>>> si = {1, 2, 3}
>>> s = si
>>> def bye():
... print("Gone with the wind...")
>>> ender = weakref.finalize(si, bye)
>>> ender.alive
True
>>> del si
>>> ender.alive
True
>>> s = None
Gone with the wind...
>>> ender.alive
False
>>> ender
<finalize object at 0x103892c70; dead>

解释:

1.模块weakref,用来创建对象的弱引用。术语referent表示使用“弱引用”引用“引用的对象”。⚠️弱引用不会被对象进行引用计数ref count。

2, si和s共同引用一个dict。

3.   当del si后,实际是删除si这个引用,而不是删除dict.

4.   当s指向其他对象时,原对象的ref count变为0,因此启动垃圾回收机制,dict被删除,内存释放。

5.   weakref.finalize(obj, func,)方法,返回一个可调用对象的终结器对象,用来管理对象的生存周期。

Weak References

不会被对象记录的引用。主要用于缓存。被弱引用所指向的对象叫做referent。

>>> class MyClass:
... pass
>>> o = MyClass()
>>> r = weakref.ref(o)
>>> r
<weakref at 0x103a63b30; to 'MyClass' at 0x103a6f790>
>>> o2 = r()
>>> o2
<__main__.MyClass object at 0x103a6f790>
>>> o is o2
True
>>> del o, o2
>>> print(r())
None
>>> r
<weakref at 0x103a63b30; dead>

使用ref(obj)创建一个弱引用r对象,  r()返回被引用的对象自身。

当两个引用都被删除,引用对象就被垃圾回收机制所删除,这时r对象,内显示一个dead标记,表示r并没有引用的对象。

需要注意的⚠️:

如果在平时使用时,不建议使用ref来创建弱引用对象,因为weakref.ref其实时一个底层接口,供高级用途使用的。比如finalize()或者weakref集合。

这个原因时因为在微观管理内存时,往往会得到意外的结果,比如不明显的隐式赋值会为对象创建新引用。

《流畅的python》的实例8-17举了一个例子:

控制台的_变量会自动绑定到结果不为None的表达式结果上,因此会对调用跟踪对象产生意料之外的引用。例子:

>>> import weakref
>>> s = {1}
>>> wref = weakref.ref(s)
>>> wref
<weakref at 0x1022a7a40; to 'set' at 0x1022a5900>
>>> wref() #_变量自动的引用了{1}。
{1}
>>> s = "hello"
>>> wref() #虽然s不在指向{1},但此时_变量还指向{1}, 因此{1}仍然存在,所以使用wref()可以返回被引用的对象{1}
{1}
>>> wref() is None #这个表达式返回False,代表此时{1}存在,但_变量重新指向了False,因此{1}的引进计数变为0,启动垃圾回收。
False
>>> wref() is None #wref()返回None。
True
>>> wref
<weakref at 0x1022a7a40; dead>

小结:

weakref其实就是配合垃圾回收来管理缓存的工具,上面讲解了弱引用的正常使用,它的原理,和背后的缺陷。并强调不要直接使用ref方法。

WeakValueDictionary类的简介

常用的管理缓存的工具类。

它的实例是一个mutable mapping, 其中value是对某个对象的弱引用。当相关的某个对象被垃圾收集后,weakValueDictionary实例中对应的key会自动的被移除。

除了WeakValueDictionary外还有WeakKeyDictionary,它的key是弱引用。

弱引用的局限

Python对象中,有些对象是不能被弱引用的:

  • int, tuple类的实例及相关子类的实例都不能弱引用。
  • dict和list类的实例不能弱引用,但子类可以。

Tricks Python Plays with Immutables

CPython的一些优化细节,对一般用户来说不重要。

>>> s1= "hi,world"
>>> s2= "hi,world"
>>> s1 is s2
False
>>> s3 = "abc"
>>> s4 = "abc"
>>> s3 is s4
True
>>> id(s3)
4362478384
>>> id(s4)
4362478384
>>> id(s1)
4363433648
>>> id(s2)
4363433264

解释:

上面s3之所以s4指向同一个对象,是因为"abc"是一个非常常用的字符串字面量,因此Python核心开发者对它做了优化,叫做interning, 扣押/驻留。

⚠️,使用==来判断,is是解释器内部常用的特征。

《流畅的Python》Object References, Mutability, and Recycling--第8章的更多相关文章

  1. 8. Object References, Mutability, and Recycling

    1. Variables Are Not Boxes # Think variables as sticky notes a = [1, 2, 3] b = a a.append(4) print b ...

  2. 流畅的python(笔记)

    流畅的python中有很多奇技淫巧,整本书都在强调如何最大限度地利用Python 标准库.介绍了很多python的不常用的数据类型.操作.库等,对于入门python后想要提升对python的认识应该有 ...

  3. Python Object Graphs — objgraph 1.7.2 documentation

    Python Object Graphs - objgraph 1.7.2 documentation Python Object Graphs¶ objgraph is a module that ...

  4. 流畅的python 对象引用 可变性和垃圾回收

    对象引用.可变性和垃圾回收 变量不是盒子 人们经常使用“变量是盒子”这样的比喻,但是这有碍于理解面向对象语言中的引用式变量.Python 变量类似于 Java 中的引用式变量,因此最好把它们理解为附加 ...

  5. 《流畅的Python》一副扑克牌中的难点

    1.现在在看<流畅的Python>这本书,看了三页就发现,这本书果然不是让新手来入门的,一些很常见的知识点能被这个作者玩出花来, 唉,我就在想,下面要分析的这些的代码,就算我费劲巴拉的看懂 ...

  6. object references an unsaved transient instance - save the transient instance before flushing错误

    异常1:not-null property references a null or transient value解决方法:将“一对多”关系中的“一”方,not-null设置为false(参考资料: ...

  7. ManyToMany【项目随笔】关于异常object references an unsaved transient instance

    在保存ManyToMany  时出现异常: org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.Tran ...

  8. Effective Java 06 Eliminate obsolete object references

    NOTE Nulling out object references should be the exception rather than the norm. Another common sour ...

  9. [SAP ABAP开发技术总结]数据引用(data references)、对象引用(object references)

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

随机推荐

  1. 网络编程(socket).WinSocket_recvfrom出错,GetLastError()为10054

    1.在写 我的Qt598(vs2017)x64版本的 shadowsocks程序时遇到的. 具体问题情况 大概是这样:QUdpSocket(假设是sktA) connect接收函数,sktA侦听 端口 ...

  2. Java中String做为synchronized同步锁

    synchronized (("" + userId).intern()) { // TODO:something } JVM内存区域里面有一块常量池,关于常量池的分配: JDK6 ...

  3. error PRJ0003 : 生成“rc.exe”时出错

    完美解决 visual studio 2008运行时 error PRJ0003 : 生成“rc.exe”时出错 步骤如下 : 1.运行vs2008安装程序,点击安装或删除程序.在“选择要安装的功能” ...

  4. [转帖]YES!AMD千元无敌U闪亮登场、16核至尊为用户着想

    YES!AMD千元无敌U闪亮登场.16核至尊为用户着想 投递人 itwriter 发布于 2019-09-30 09:34 评论(0) 有567人阅读 原文链接 [收藏] « » https://ne ...

  5. PAT甲级 二叉查找树 相关题_C++题解

    二叉查找树 PAT (Advanced Level) Practice 二叉查找树 相关题 目录 <算法笔记> 重点摘要 1099 Build A Binary Search Tree ( ...

  6. 第一个vue程序:hello,vlue

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  7. python中集合set,字典dict和列表list的区别以及用法

    python中set代表集合,list代表列表,dict代表字典 set和dict的区别在于,dict是存储key-value,每一个key都是唯一的,set相对于dict存储的是key,且key是唯 ...

  8. 设计Qt风格的C++API

    在奇趣(Trolltech),为了改进Qt的开发体验,我们做了大量的研究.这篇文章里,我打算分享一些我们的发现,以及一些我们在设计Qt4时用到的原则,并且展示如何把这些原则应用到你的代码里. 优秀AP ...

  9. PLSQL Developer、汉化包官方下载及注册码

    1.官方下载地址 https://www.allroundautomations.com/registered/plsqldev.html 2.找到对应安装包和语言包 3.先安装PLSQL Devel ...

  10. MacBook使用HHKB键盘设置

    问题: macbook上使用hhkb的时候,很纠结档位4要不要开,开启4号DIP开关后,虽然会使HHKB键盘上减少一个“◇(Win键)”键,但是会在键盘左侧多出一个“FN”键.多出来的左“FN”键,不 ...