流畅的python8:对象引用,可变性和垃圾回收

8.1变量不是盒子

@ 变量是别名

 

8.2标识,相等性和别名

@使用is ,而不是比较id()相等

@注意,在不同的解释器中,id()返回的不一定是内存地址,但一定是唯一的标注,且在变量生命周期中不变。

 

8.2.1 在==和 is 之间选择

@==标记值相等,而 is 比较id()相等

@ == 调用的是__eq__()方法

 

8.2.2 元组的相对不可变性

@元组不变是指元组的引用不变。

 

8.3 默认做浅赋值

@注意列表的复制是浅复制

 

8.3 为对象做深复制和浅复制

@使用copy.deepcopy( ) 实现深复制,deepcopy会记住已经复制的对象,避免的循环引用导致的无限循环。

@深复制有时可能太深了,对象可能引用不该复制的外部资源或单例值。我们可以实现特殊方法__copy__() 和 __deepcopy__(),控制 copy 和 deepcopy 的行为

 

 

8.4 函数的参数作为引用时

@无

 

8.4.1 不要使用可变类型作为参数的默认值

@假设新建类A,使用默认空列表作为一个__init__的一个默认参数,这样会导致问题:所有的类A实例都引用的是同一个列表。而且这个BUG很难发现。

@这里的问题是:默认值在定义 函数时计算(通常在加载模块时),因此默认值变成了函数对象的属性。如果默认 值是可变对象,而且修改了它的值,那么后续的函数调用都会受到影响。

@__init__ 方法检查 passengers 参数的值是不是 None,如果是就把一个新的 空列表赋值给 self.passengers。如果 passengers 不是 None,正确的实现 会把 passengers 的副本赋值给 self.passengers。

 

8.4.2防御可变参数

@在类的内部维护默认参数的可变类型。

@除非这个方法确实想修改通过参数传入的对象,否则在类中直接把参数赋值 给实例变量之前一定要三思,因为这样会为参数对象创建别名。如果不确 定,那就创建副本。这样客户会少些麻烦。

 

8.5 del和垃圾回收

@有个 __del__ 特殊方法,但是它不会销毁实例,不应该在代码中调用。即将销 毁实例时,Python 解释器会调用 __del__ 方法,给实例最后的机会,释放外部 资源

@。自己编写的代码很少需要实现 __del__ 代码,有些 Python 新手会花时 间实现,但却吃力不讨好,因为 __del__ 很难用对

@weakref包:弱引用

 

8.6 弱引用

@有时需要引用对象,而不让对象存在的时间超过所需时间。这经常用在 缓存中。

@弱引用不会增加对象的引用数量。引用的目标对象称为所指对象(referent)。因此我们说, 弱引用不会妨碍所指对象被当作垃圾回收。

@弱引用在缓存应用中很有用,因为我们不想仅因为被缓存引用着而始终保存缓存对象。

@weakref.ref 类其 实是低层接口,供高级用途使用,多数程序最好使用 weakref 集合和 finalize。也就是说, 应该使用 WeakKeyDictionary、WeakValueDictionary、WeakSet 和 finalize(在内部使用弱 引用),不要自己动手创建并处理 weakref.ref 实例。

 

8.6.1 WeakValueDictionary简介

@WeakValueDictionary 类实现的是一种可变映射,里面的值是对象的弱引用。被引用的对象 在程序中的其他地方被当作垃圾回收后,对应的键会自动从 WeakValueDictionary 中删除。

@与 WeakValueDictionary 对应的是WeakKeyDictionary, 后 者 的 键 是 弱 引 用: (WeakKeyDictionary 实例)可以为应用中其他部分拥有的对象附加数据,这样就无 需为对象添加属性。这对覆盖属性访问权限的对象尤其有用。

@weakref 模块还提供了 WeakSet 类,按照文档的说明,这个类的作用很简单:“保存元素弱 引用的集合类。元素没有强引用时,集合会把它删除。”如果一个类需要知道所有实例, 一种好的方案是创建一个 WeakSet 类型的类属性,保存实例的引用。如果使用常规的 set, 实例永远不会被垃圾回收,因为类中有实例的强引用,而类存在的时间与 Python 进程一样 长,除非显式删除类。

@这些集合,以及一般的弱引用,能处理的对象类型有限。

 

8.6.2 弱引用的局限

@不是每个python对象都可以作为弱引用的目标。

@基本的list 和 dict实例不能作为所指对象,但是他们的子类可以。基本set 实例可以作为所指对象,用户定义的类型也没问题,但int和tuple实例不能作为弱引用的目标,甚至他们的子类也不行。这些局限基本上是cpython的实现细节,在其他的python解释器中情况可能完全不一样。这是解释器内部优化的结果。

8.7 python对不可变类型施加的把戏。

@使用元组构建元组,得到其实是同一个元组。

@str,bytes和frozenset实例也有这种行为,X.copy( )返回的是同一个对象的引用,而不是创建一个副本。

 

8.8本章小结

@使用可变类型作为函数参数的默认值有危险,因为如果就地修改了参数,默认值也就改变了,这回影响以后使用默认值的调用。

@某些情况下,可能需要保存对象的引用,而不是对象本身。这是可以使用弱引用,这是一种底层机制,是weakref模块中weakvaluedictionary,weakkeydictionary和weakset等有用的集合类。以及finalize函数的底层支持。

 

杂谈:

@java的==比较的引用,即使是判断字符串相等也要使用.equal()函数,而python == 比较的是值相等,is判断的是引用相等,与c# 一样

@尽管这样写:open('test.txt', 'wt', encoding='utf-8').write('1, 2, 3') 是安全的,但在其他解释器下面不一定是正确的,例如Jython使用的垃圾回收策略不是引用计数,这会导致write执行时,垃圾回收程序将open返回的句柄销毁掉了。正确的做法是使用with语句,她保证文件一定会被关闭。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值