记录学习《流畅的python》的一些知识-----对象引用,可变性和垃圾回收
记录我学习《流畅的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》的一些知识-----对象引用,可变性和垃圾回收相关推荐
- 流畅的python 对象引用 可变性和垃圾回收
对象引用.可变性和垃圾回收 变量不是盒子 人们经常使用"变量是盒子"这样的比喻,但是这有碍于理解面向对象语言中的引用式变量.Python 变量类似于 Java 中的引用式变量,因此 ...
- 《Fluent Python》学习笔记:第 8 章 对象引用、可变性和垃圾回收
本文主要是 Fluent Python 第 8 章的学习笔记.这部分主要是介绍了变量.引用.对象.深拷贝.浅拷贝.垃圾回收等.本章虽然枯燥,但是非常有用. <Fluent Python>学 ...
- [流畅的Python][8][对象引用、可变性和垃圾回收]
第8章 对象引用.可变性和垃圾回收 "你不开心,"白骑士用一种忧虑的声调说,"让我给你唱一首歌安 慰你吧--这首歌的曲名叫作 :<黑线鳕的眼睛>." ...
- 流畅的Python读书笔记-第八章-对象引用、可变性和垃圾回收
第8章:对象引用,可变性和垃圾回收 在Python里面变量不是盒子,而是便利贴,类似于Java中的引用变量,因此最好把它们理解为附加在对象上的标注. 因为变量不过是标注,因此无法阻止为对象贴上多个标注 ...
- python基础编程:基于Python对象引用、可变性和垃圾回收详解
下面小编就为大家带来一篇基于Python对象引用.可变性和垃圾回收详解.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 变量不是盒子 在示例所示的交互式控制台中,无法使用 ...
- 【学习笔记】Python 基础零碎知识
Python 基础零碎知识 条件表达式 比如上面的if条件判断,我们可以用条件表达式(Conditional Expression)更加简洁的书写. y = math.log(x) if x > ...
- 【学习笔记】Python基础入门知识笔记,万字攻略带你走进Python编程
Python笔记,由此开始吧 本文是笔者在学习Python过程做的笔记总结,将简要讲述Python的基础知识,内容清晰易理解,相信你也能轻松的学会Python的入门基础知识内容.同时也希望这篇文章 ...
- Python学习:对象引用、可变性和垃圾回收
1.Python中的变量是什么 在示例所示的交互式控制台中,无法使用"变量是盒子"做解释.下图说明了在 Python 中为什么不能使用盒子比喻,而便利贴则指出了变量的正确工作方式. ...
- python用可变参数求积_流畅的python读书笔记-第八章-对象引用、可变性和垃圾回收...
对象不是个盒子 class Gizmo: def __init__(self): print('Gizmo id: %d' % id(self)) x = Gizmo() print(x) y = G ...
最新文章
- 《R语言游戏数据分析与挖掘》一3.4 小结
- Apache Shiro实现用户登录功能
- 【ASP.NET】登陆成功后如何跳转到上一个页面
- Prometheus学习
- rabbit和mysql事务_分布式事务原理及SpringBoot整合RabbitMQ实现可靠事件,TCC事务模型及接口幂等性...
- 2017.2.10自测(noip2002)
- java吃货联盟系统源码_小项目,吃货联盟,java初级小项目,源代码
- 普通摄像头游戏——空中飞车
- webpack配置路径及hash版本号,利用html-webpack-plugin自动生成html模板
- 网页自动填表html,韶关网页自动填表基础教程从简单开始 - MutouSoft - 时间财富网...
- 【python第三方库】playwright简要入门
- vant 表单按钮置灰_Vant Switch 开关
- vscode 源代码管理窗口显示空白
- 初探深度优化搜索--小白版
- 中兴以太网板see服务器地址,中兴传输以太网板数据配置.ppt
- Stereo Vision-based Semantic 3D Object and Ego-motion Tracking for Autonomous Driving
- 日语五十音图(带图)
- Windows-驱动-解决Thinkpad e470c在Win8.1下WiFi无法连接的问题
- 新松机器人发行价_机器人公司简介,沈阳新松机器人自动化股份有限公司企业概况_赢家财富网...
- 关于激光打标机矢量图下载的说明