第8章 对象引用、可变性和垃圾回收
2018-01-08 21:53
387 查看
8.1 变量不是盒子
变量,我们可以理解为附加在对象上的标注,在创建对象后才把变量分配给对象。因为变量只是标注,所以对象可以贴多个标注。贴的多个标注,就是别名。
8.2 标识、相等性和别名
示例8-3 charles和lewis指代一个对象
示例8-4 alex与charles比较的结果是相等,但alex不是charles
元组的相对不可变性
元组的不可变是指本身的引用不会变,但元素可以变。
8.3 默认做浅复制
复制分浅复制和深复制。浅复制是复制了外壳,但本身的引用未改变。深复制是完成创建了一个新的对象。
示例8-9 使用copy和deepcopy产生的影响
8.4 函数的参数作为引用时
Python唯一支持的参数传递模式是共享传参。多数面向对象语言都采用这一模式。共享传参指函数的各个形式参数获得实参中各个引用的副本。也就是说,形参是实参的别名。
这个情况下,函数可能会修改作为参数传入的可变对象,但无法修改对象的标识。
不要使用可变类型作为参数的默认值
如果默认值是可变对象,而且修改了它的值,那么后续的函数调用都会受到影响。
示例8-13 备受幽灵乘客折磨的校车
防御可变参数
如果定义的函数接收可变参数,应该谨慎考虑调用方师傅期望修改传入的参数。
示例8-15 一个简单类,说明接收可变参数的风险
正确做法:
8.5 del和垃圾回收
del语句删除名称,而不是对象。
8.6 弱引用
正是因为有引用,对象才会在内存中存在。当对象的引用数量归零后,垃圾回收程序会把对象销毁。但是,有时需要引用对象,而不让对象存在的时间超过所需时间。这精彩用在缓存中。
弱引用不会增加对象的引用数。因此,若引用不会妨碍所指对象被当成垃圾回收。
示例8-17 弱引用是可调用的对象,返回的是被引用的对象;如果所指对象不存在了,返回None
8.7 Python对不可变类型施加的把戏
***
8.8 本章小结
***
变量,我们可以理解为附加在对象上的标注,在创建对象后才把变量分配给对象。因为变量只是标注,所以对象可以贴多个标注。贴的多个标注,就是别名。
8.2 标识、相等性和别名
示例8-3 charles和lewis指代一个对象
>>> charles = {'name':'Charles L. Dodgson', 'born':1832} >>> lewis = charles #lewis是charles的别名 >>> lewis is charles True >>> id(charles), id(lewis) #is运算符和id函数确认这点 (46892400, 46892400) >>> lewis['balance'] = 950 >>> charles {'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
示例8-4 alex与charles比较的结果是相等,但alex不是charles
>>> alex = {'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950} >>> alex == charles True >>> alex is not charles True
元组的相对不可变性
元组的不可变是指本身的引用不会变,但元素可以变。
8.3 默认做浅复制
复制分浅复制和深复制。浅复制是复制了外壳,但本身的引用未改变。深复制是完成创建了一个新的对象。
示例8-9 使用copy和deepcopy产生的影响
>>> 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) >>> id(bus1),id(bus2),id(bus3) #创建3个不同的Bus实例 (47783280, 47849936, 47850608) >>> bus1.drop('Bill') >>> bus2.passengers #bus1的'Bill'下车后,bus2也没有他了 ['Alice', 'Claire', 'David'] >>> id(bus1.passengers),id(bus2.passengers),id(bus3.passengers) #审查passengers属性后发现,bus1和bus2共享一个列表对象,因为bus2是bus1的浅复制副本。 (47069904, 47069904, 47787984) >>> bus3.passengers #bus3是bus1的深复制副本,因此它的passengers属性指代另一个列表。 ['Alice', 'Bill', 'Claire', 'David']
8.4 函数的参数作为引用时
Python唯一支持的参数传递模式是共享传参。多数面向对象语言都采用这一模式。共享传参指函数的各个形式参数获得实参中各个引用的副本。也就是说,形参是实参的别名。
这个情况下,函数可能会修改作为参数传入的可变对象,但无法修改对象的标识。
>>> def f(a,b): a += b return a >>> x = 1 >>> y = 2 >>> f(x,y) 3 >>> x,y #x,y未变 (1, 2) >>> a = [1,2] >>> b = [3,4] >>> f(a,b) [1, 2, 3, 4] >>> a,b #列表a变了 ([1, 2, 3, 4], [3, 4]) >>> t = (10,20) >>> u = (30,40) >>> f(t,u) (10, 20, 30, 40) >>> t,u #元组t没变 ((10, 20), (30, 40))
不要使用可变类型作为参数的默认值
如果默认值是可变对象,而且修改了它的值,那么后续的函数调用都会受到影响。
示例8-13 备受幽灵乘客折磨的校车
>>> class HauntedBus: """备受幽灵乘客折磨的校车""" def __init__(self, passengers=[]): self.passengers = passengers def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name) >>> bus1 = HauntedBus(['Alice', 'Bill']) >>> bus1.passengers ['Alice', 'Bill'] >>> bus1.pick('Charlie') >>> bus1.drop('Alice') >>> bus1.passengers ['Bill', 'Charlie'] >>> bus2 = HauntedBus() >>> bus2.pick('Carrie') >>> bus2.passengers ['Carrie'] >>> bus3 = HauntedBus() >>> bus3.passengers ['Carrie'] >>> bus3.pick('Dave') >>> bus2.passengers ['Carrie', 'Dave'] >>> bus2.passengers is bus3.passengers True >>> bus1.passengers ['Bill', 'Charlie']问题在于,没有指定初始乘客的HauntedBus实例会共享一个乘客列表。所以,通常使用None作为接收可变值的默认值。
防御可变参数
如果定义的函数接收可变参数,应该谨慎考虑调用方师傅期望修改传入的参数。
示例8-15 一个简单类,说明接收可变参数的风险
class TwilightBus: """备受幽灵乘客折磨的校车""" def __init__(self, passengers=None): self.passengers = [] else: self.passengers = passengers #这个赋值是把self.passengers变成passengers的别名,而后者是传给__init__方法的实参的别名。 def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name) #remove()和append()方法会修改传给构造器方法的那个列表
正确做法:
def __init__(self, passengers=None): self.passengers = [] else: self.passengers = list(passengers) #创建passengers列表的副本;如果不是列表,就把它转换成列表。
8.5 del和垃圾回收
del语句删除名称,而不是对象。
8.6 弱引用
正是因为有引用,对象才会在内存中存在。当对象的引用数量归零后,垃圾回收程序会把对象销毁。但是,有时需要引用对象,而不让对象存在的时间超过所需时间。这精彩用在缓存中。
弱引用不会增加对象的引用数。因此,若引用不会妨碍所指对象被当成垃圾回收。
示例8-17 弱引用是可调用的对象,返回的是被引用的对象;如果所指对象不存在了,返回None
>>> import weakref >>> a_set = {0,1} >>> wref = weakref.ref(a_set) #创建弱引用对象wref,下一行审查它 >>> wref <weakref at 0x02D0FD20; to 'set' at 0x02CF7738> >>> wref() #调用wref()返回的是被引用的对象,{0,1}。因为这是控制台会话,所以{0,1}会绑定给_变量。 {0, 1} >>> a_set = {2,3,4} #a_set不再指代{0,1}集合,因此集合的引用数量减少了。但是,_变量仍然指代它。 >>> wref() #调用wref()依旧返回{0,1} {0, 1} >>> wref() is None #计算这个表达式时,{0,1}存在,因此wref()不是None。但是,随后_绑定到结果值False。现在{0,1}没有强引用了。 False >>> wref() is None #因为{0,1}对象不存在了,所以wref()返回None。 True
8.7 Python对不可变类型施加的把戏
***
8.8 本章小结
***
相关文章推荐
- Python 对象引用、可变性和垃圾回收
- 基于Python对象引用、可变性和垃圾回收详解
- 流程的Python 第八章:对象引用、可变性和垃圾回收
- 流畅的python第八章对象引用,可变性和垃圾回收
- 面向对象_引用类型_内存分析_垃圾回收JAVA028-033
- Java基础复习笔记 对象状态、引用种类、垃圾回收形式02
- 对象的垃圾回收和四种引用
- javascript基础(对象,对象属性,属性基本和引用数据类型,字面量创建对象,垃圾回收,属性的枚举)(十三)
- 第三章 JVM内存回收区域+对象存活的判断+引用类型+垃圾回收线程
- 表示弱引用,即在引用对象的同时仍然允许垃圾回收来回收该对象。
- Java虚拟机垃圾回收(一) 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析
- 垃圾回收机制中,引入计数是如何实现的,内部原理是什么,怎么维持对象引用的
- JVM内存回收区域+对象存活的判断+引用类型+垃圾回收线程
- Java虚拟机笔记(二):GC垃圾回收和对象的引用
- jvm(三):对象引用与垃圾回收
- Java虚拟机垃圾回收(一) 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析
- jvm垃圾收集器回收什么样的对象以及各种引用
- 两个互相引用对象的垃圾回收
- 垃圾回收之判断对象否需要被回收(根搜索算法和引用搜索算法)
- jvm什么时候回收垃圾对象---引用计数和GC root