记录我学习《流畅的python》的过程--对象引用,可变性和垃圾回收

  • 2021.9.22
  • 1.变量不是盒子
  • 2.标识、相等性和别名
  • 3.默认做浅复制
  • 4.函数的参数作为引用时
  • 5.del和垃圾回收
  • 6.弱引用

2021.9.22

1.变量不是盒子

如果把变量想象为盒子,那么无法解释Python中的赋值;应该把变量视作便利贴,这样图片中的行为就好解释了。

a = [1, 2, 3]
b = a
a.append(4)
print(b)

运行结果:

如果是b中添加4,输出a,结果一样。因为变量a和b引用同一个列表,而不是那个列表的副本。
对引用变量来说,说把变量分配给对象更合理,反过来说就有问题。毕竟对象在赋值之前就创建了。

class Gizmo:def __init__(self):print('Gizmo id: %d' % id(self))x = Gizmo()print(x)y = Gizmo * 10print(y)

运行结果:

在乘法运算中使用Gizmo实例会抛出异常。因为在尝试求积之前其实会创建一个新的Gizmo实例。但是肯定不会创建变量y,因为在对赋值语句的右边进行求值时抛出了异常。

2.标识、相等性和别名

charles和lewis指代同一个对象

charless = {'name': 'Charles L. Dodgson', 'born': 1832}
lewis = charless
x = lewis is charless
print(x)print(id(charless), id(lewis))
lewis['balance'] = 950print(charless)

运行结果:

程序证明lewis是charless的别名。is函数和id函数确认了这一点。向lewis中添加一个元素相当于charless中添加了一个元素。

如果一个博士的信息与charless相同,他们比较的结果是相同的,但是他们并不相同,他们具有相同的值(==比较的就是值),但是它们的标识不同。

charless = {'name': 'Charles L. Dodgson', 'born': 1832}
lewis = charless
x = lewis is charless
print(x)print(id(charless), id(lewis))
lewis['balance'] = 950print(charless)alex = {'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
y = alex == charless
print(y)
z = alex is not charless
print(z)

result:

2.1在==和is之间选择

= =运算符比较两个对象的值(对象中保存的数据),而is比较对象的标识。通常我们关注的是值,而不是标识。
在变量和单例值之间比较时,应该使用is。目前,最常使用is检查变量绑定的值是不是None。写法:
x is None
否定:
x is not None
is运算符比= =速度快,因为它不能重载,所以Python不用寻找并调用特殊方法,而是直接比较两个正数ID。而a==b是语法糖,等同于a.__ eq __(b)。继承自object的 __ eq __方法比较两个对象的ID,结果与is一样。相等性测试可能设计大量处理工作,例如,比较大型集合或嵌套层级深的结构时。

2.2元组的相对不可变性
元组与多数Python集合(列表,字典,集等等)一样,保存的是对象的引用。如果引用的元素是可变的,即便元素本身不可变,元素依然可变。也就是说,元组的不可变性其实是指tuple数据结构的物理内容(即保存的引用)不可变,与引用的对象无关。
下面举例说明元组的值会随着引用的可变对象的变化而变。元组中不可变的是元素的标识。

t1 = (1, 2, [30, 40])
t2 = (1, 2, [30, 40])
x = t1 == t2
print(x)print(id(t1[-1]))t1[-1].append(99)
print(t1)print(id(t1[-1]))y = t1 == t2
print(y)

Result:

元组t1是不可变的,但是其中的列表是可变的,当列表改变时,标识没有变,只是值变了,这时t1和t2也不同了。

3.默认做浅复制

复制列表最简单的方式是使用内置的类型构造方法,例如:

l1 = [3, [55, 44], (7, 8, 9)]
l2 = list(l1)
print(l2)
print(l2 == l1)
print(l2 is l1)


l2是创建l1的副本,第二条结果表明两者的源列表相等,第三天结果表示两者并不相等。对列表和其他可变序列来说,还能使用简洁的l2 = l1[:]语句创建副本,但是这些都是浅复制,即复制了最外层容器,副本中的元素是源容器中元素的引用,如果所有元素都是不可变的,那么没有问题,但是如果有可变的元素,会发生问题。例如:

l1 = [3, [55, 44], (7, 8, 9)]
l2 = list(l1)
l1.append(100)
l1[1].remove(55)
print('l1:', l1)
print('l2:', l2)
l2[1] += [33, 22]
l2[2] += (10, 11)
print('l1:', l1)
print('l2:', l2)


l2是l1的浅复制,把100追加到l1中,对l2没有影响。把内部列表l1[1]中的55删除,这对l2有影响,因为l2[1]绑定的列表与l1[1]是同一个。对可变的对象来说,如l2[1]引用的列表,+=运算符就地修改列表。这次修改在l1[1]中也有体现,因为它是l2[1]的别名。对元组来说,+=运算符创建一个新元组,然后重新绑定给变量l2[2]。这等同于l2[2] = l2[2] + (10, 11)。现在,l1和l2中最后位置上的元组不是同一个对象。

为任意对象做浅复制和深复制
深浅复制的区别是是否共享内部对象的引用,copy模块提供的deepcopy和copy函数能为任意对象做深复制和浅复制。

class Bus:def __init__(self, passengers=None):if passengers is None:self.passengers = []else:self.passengers = list(passengers)def pick(self, name):self.passengers.append(name)def drop(self, name):self.passengers.remove(name)import copy
bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
bus2 = copy.copy(bus1)
bus3 = copy.deepcopy(bus1)print(id(bus1), id(bus2), id(bus3))
bus1.drop('Bill')
print(bus2.passengers)
print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers))
print(bus3.passengers)

循环引用:

a = [10, 20]
b = [a, 30]
a.append(b)
print(a)
from copy import deepcopy
c = deepcopy(a)
print(c)

4.函数的参数作为引用时

下面的函数在参数上调用+=运算符。分别把数字、列表和元组传给那个函数,实际传入的实参会以不同的方式受到影响。
函数可能会修改接收到的任何可变对象。

def f(a, b):a += breturn ax = 1
y = 2
print(f(x, y))
print(x, y)a = [1, 2]
b = [3, 4]
print(f(a, b))
print(a, b)t = (10, 20)
u = (30, 40)
print(f(t, u))
print(t, u)


运行结果表示数字x和元组t没变,列表a变了。

下面说明避免使用可变的对象作为参数的默认值。

class HauntedBus:"""备受幽灵乘客折磨的校车"""def __init__(self, passengers=[]):self.passengers = passengersdef pick(self, name):self.passengers.append(name)def drop(self, name):self.passengers.remove(name)bus1 = HauntedBus(['Alice', 'Bill'])
print(bus1.passengers)bus1.pick('Charlie')
bus1.drop('Alice')
print(bus1.passengers)bus2 = HauntedBus()
bus2.pick('Carrie')
print(bus2.passengers)bus3 = HauntedBus()
print(bus3.passengers)
bus3.pick('Dave')
print(bus2.passengers)print(bus2.passengers is bus3.passengers)
print(bus1.passengers)

当bus2是空的时候,赋值给self.passengers。bus3一开始是空的,因此还是赋值默认的列表,但是默认列表不是空的,现在bus2和bus3指代同一个列表,但bus1.passenger是不同的列表。

防御可变参数

class TwilighitBus:"""让乘客销声匿迹的校车"""def __init__(self, passengers=None):if passengers is None:self.passengers = []else:self.passengers = passengersdef pick(self, name):self.passengers.append(name)def drop(self, name):self.passengers.remove(name)basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
bus = TwilighitBus(basketball_team)
bus.drop('Tina')
bus.drop('Pat')
print(basketball_team)


在内部这样处理乘客列表,就不会影响初始化校车时传入的参数了。

5.del和垃圾回收

没有指向对象的引用时,监视对象生命结束的情形

import weakref
s1 = {1, 2, 3}
s2 = s1
def bye():print('Gone with the wind...')ender = weakref.finalize(s1, bye)
print(ender.alive)del s1
print(ender.alive)s2 = 'spam'
print(ender.alive)

del不会删除对象,但是执行del操作后可能会导致对象不可获取,从而被删除。

6.弱引用

正是因为有引用,对象才会在内存中存在。当对象的引用数量归零后,垃圾回收程序会把对象销毁。但是,有时需要引用对象,而不让对象存在的时间超过所需时间。这经常在缓存中。
弱引用不会增加对象的引用数量,这在缓存应用中很有用,因为我们不像仅因为被缓存引用着而始终保存缓存对象。

弱引用是可调用的对象,返回的是被引用的对象,如果所指对象不存在了,返回None。

import weakref
a_set = {0, 1}
wref = weakref.ref(a_set)
print(wref)print(wref())a_set = {2, 3, 4}
print(wref())print(wref() is None)
print(wref() is None)


WeakValueDictionary类实现的是一种可变映射,里面的值是对象的弱引用。

class Cheese:def __init__(self, kind):self.kind = kinddef __repr__(self):return 'Cheese(%r)' % self.kindimport weakref
stock = weakref.WeakValueDictionary()
catalog = [Cheese('Red Leicester'), Cheese('Tilsit'), Cheese('Brie'), Cheese('Parmesan')]for cheese in catalog:stock[cheese.kind] = cheeseprint(sorted(stock.keys()))
del catalog
print(sorted(stock.keys()))
del cheese
print(sorted(stock.keys()))

记录学习《流畅的python》的一些知识-----对象引用,可变性和垃圾回收相关推荐

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

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

  2. 《Fluent Python》学习笔记:第 8 章 对象引用、可变性和垃圾回收

    本文主要是 Fluent Python 第 8 章的学习笔记.这部分主要是介绍了变量.引用.对象.深拷贝.浅拷贝.垃圾回收等.本章虽然枯燥,但是非常有用. <Fluent Python>学 ...

  3. [流畅的Python][8][对象引用、可变性和垃圾回收]

    第8章 对象引用.可变性和垃圾回收 "你不开心,"白骑士用一种忧虑的声调说,"让我给你唱一首歌安 慰你吧--这首歌的曲名叫作 :<黑线鳕的眼睛>." ...

  4. 流畅的Python读书笔记-第八章-对象引用、可变性和垃圾回收

    第8章:对象引用,可变性和垃圾回收 在Python里面变量不是盒子,而是便利贴,类似于Java中的引用变量,因此最好把它们理解为附加在对象上的标注. 因为变量不过是标注,因此无法阻止为对象贴上多个标注 ...

  5. python基础编程:基于Python对象引用、可变性和垃圾回收详解

    下面小编就为大家带来一篇基于Python对象引用.可变性和垃圾回收详解.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 变量不是盒子 在示例所示的交互式控制台中,无法使用 ...

  6. 【学习笔记】Python 基础零碎知识

    Python 基础零碎知识 条件表达式 比如上面的if条件判断,我们可以用条件表达式(Conditional Expression)更加简洁的书写. y = math.log(x) if x > ...

  7. 【学习笔记】Python基础入门知识笔记,万字攻略带你走进Python编程

    ​ Python笔记,由此开始吧 本文是笔者在学习Python过程做的笔记总结,将简要讲述Python的基础知识,内容清晰易理解,相信你也能轻松的学会Python的入门基础知识内容.同时也希望这篇文章 ...

  8. Python学习:对象引用、可变性和垃圾回收

    1.Python中的变量是什么 在示例所示的交互式控制台中,无法使用"变量是盒子"做解释.下图说明了在 Python 中为什么不能使用盒子比喻,而便利贴则指出了变量的正确工作方式. ...

  9. python用可变参数求积_流畅的python读书笔记-第八章-对象引用、可变性和垃圾回收...

    对象不是个盒子 class Gizmo: def __init__(self): print('Gizmo id: %d' % id(self)) x = Gizmo() print(x) y = G ...

最新文章

  1. 《R语言游戏数据分析与挖掘》一3.4 小结
  2. Apache Shiro实现用户登录功能
  3. 【ASP.NET】登陆成功后如何跳转到上一个页面
  4. Prometheus学习
  5. rabbit和mysql事务_分布式事务原理及SpringBoot整合RabbitMQ实现可靠事件,TCC事务模型及接口幂等性...
  6. 2017.2.10自测(noip2002)
  7. java吃货联盟系统源码_小项目,吃货联盟,java初级小项目,源代码
  8. 普通摄像头游戏——空中飞车
  9. webpack配置路径及hash版本号,利用html-webpack-plugin自动生成html模板
  10. 网页自动填表html,韶关网页自动填表基础教程从简单开始 - MutouSoft - 时间财富网...
  11. 【python第三方库】playwright简要入门
  12. vant 表单按钮置灰_Vant Switch 开关
  13. vscode 源代码管理窗口显示空白
  14. 初探深度优化搜索--小白版
  15. 中兴以太网板see服务器地址,中兴传输以太网板数据配置.ppt
  16. Stereo Vision-based Semantic 3D Object and Ego-motion Tracking for Autonomous Driving
  17. 日语五十音图(带图)
  18. Windows-驱动-解决Thinkpad e470c在Win8.1下WiFi无法连接的问题
  19. 新松机器人发行价_机器人公司简介,沈阳新松机器人自动化股份有限公司企业概况_赢家财富网...
  20. 关于激光打标机矢量图下载的说明

热门文章

  1. 什么是自动化运维?自动化运维必备技能有哪些?
  2. matlab 格兰杰,matlab非参数的格兰杰因果分析
  3. 拒绝踩坑!源码编译 tensorflow 解决 cuda 不配套 万金油方法
  4. 我们在使用领英时有必要用领英精灵吗?
  5. SpringBoot启动图案大全
  6. XDOJ 分配宝藏 C
  7. python django 安装虚拟环境创建工程项目
  8. spring.jpa.hibernate.naming.physical-strategy
  9. javascript基础复习之函数,定时器,erval函数
  10. Facebook创始人——扎克伯格