您的位置:首页 > 编程语言 > Python开发

第8章 对象引用、可变性和垃圾回收

2018-01-08 21:53 387 查看
8.1 变量不是盒子
变量,我们可以理解为附加在对象上的标注,在创建对象后才把变量分配给对象。因为变量只是标注,所以对象可以贴多个标注。贴的多个标注,就是别名。

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
相关文章推荐