Fluent Python第八章 对象引用、可变性、垃圾回收

本文详细探讨了Python中变量标识、相等性比较(==与is)、元组的相对不可变性,以及默认浅复制与深复制的区别。通过实例展示了如何使用is和id函数判断对象关系,以及copy和deepcopy在复制列表和复杂对象时的作用。

变量是标注,而不是盒子

8.2标识、相等性和别名

In [1]: a={'name':"Big","born":1960}                                                                                                   

In [2]: b=a                                                                                                                            

In [3]: a is b                                                                                                                         
Out[3]: True

In [4]: b is a                                                                                                                         
Out[4]: True

In [5]: id(a)                                                                                                                          
Out[5]: 139906931798496

In [6]: id(b)                                                                                                                          
Out[6]: 139906931798496

(1)这里首先告诉我们,b是a的别名
(2)is运算符和id函数确认了这一点
(3)向b中添加一个元素就相当于向a中添加一个元素

In [7]: c={'name':"Big","born":1960}                                                                                                   

In [8]: c == a                                                                                                                         
Out[8]: True

In [9]: c is a                                                                                                                         
Out[9]: False

In [10]: c is b                                                                                                                        
Out[10]: False

In [11]: c is not a                                                                                                                    
Out[11]: True

(1)这里又告诉我们c指代的对象和赋值给a的对象内容相同(==比较的就是值)
(2)比较两个对象,结果相等,这是因为dict类的__eq__方法就是这样实现的(比较对象的值)
(3)但是它们是不同的对象,因为它们的标识—内存中的地址是不同的

每个变量都有标识、类型和值。对象一旦创建,它的标识绝不会变;你可以把标识理解为对象在内存中的地址。is运算符比较两个对象的标识;id()函数返回对象标识的整数表示。

注意

在CPython中,id()返回对象的内存地址,但是在其他python解释器中的值可能不同。最关键的就是,ID一定是唯一的数值标注,在对象的生命周期中很少改变。

8.2.1在==和is之间选择

==运算符比较两个对象的值(对象中保存的数据),而is比较对象的标识。

is 运算符比==速度快,因为它不能重载,所以Python不用寻找你并调用特殊方法,而是直接比较两个整数ID。

a == b是语法糖,等同于a.__eq__(b)。继承自object__eq__方法比较两个对象的ID,结果与is一样。但是多数内置类型使用更加有意义的方式覆盖了__eq__方法,会考虑对象属性的值。

8.2.2元组的相对不可变性

元组与多数Python集合(列表、字典、集,等等)一样,保存的是对象的引用。

如果引用的元素是可变的,即便元组本身不可变,元素依然可变。也就是说,元组的不可变性其实是指tuple数据结构的物理内容(即保存的引用)不可变,与引用的对象无关。

In [38]: t1=(1,2,[30,40])                                                                                                              

In [39]: t2=(1,2,[30,40])                                                                                                              

In [40]: id(t1)                                                                                                                        
Out[40]: 139906931856960

In [41]: id(t2)                                                                                                                        
Out[41]: 139906931931808

In [42]: t1 is t2                                                                                                                      
Out[42]: False

In [43]: t1 == t2                                                                                                                      
Out[43]: True

In [44]: id(t1[-1])                                                                                                                    
Out[44]: 139907286104016

In [45]: id(t2[-1])                                                                                                                    
Out[45]: 139907293061600

In [46]: t1[-1].append(99)                                                                                                             

In [47]: t1                                                                                                                            
Out[47]: (1, 2, [30, 40, 99])

In [48]: id(t1[-1])                                                                                                                    
Out[48]: 139907286104016

In [49]: t1==t2                                                                                                                        
Out[49]: False

In [50]: t1 is t2                                                                                                                      
Out[50]: False

从这里我们可以看到,t1不可变,但是t1[-1]可变;t1t2是不同的对象,但是二者值相等。
t1[-1]中增加元素之后,t1[-1]的标识没有变化,但是值变了。

8.3默认做浅复制

复制列表(或者多数内置的可变集合)最简单的方式是使用内置的类型构造方法

In [51]: l1=[3,[55,44],(7,8,9)]                                                                                                        

In [52]: l2=list(l1)                                                                                                                   

In [53]: l2                                                                                                                            
Out[53]: [3, [55, 44], (7, 8, 9)]

In [55]: l2 == l1                                                                                                                      
Out[55]: True

In [56]:  l2 is l1                                                                                                                     
Out[56]: False

这里把l1list类型复制给l2list(l1)创建了l1的副本。副本l2与原列表l1值相同,但是二者指待了不同的对象。

还可以使用构造方法或者[:]做浅复制(即复制了最外层的容器,副本中的元素是源容器中的元素引用)。
在这里插入图片描述

8.3.1为任意对象做深复制和浅复制

深复制(即副本不共享内部对象的引用)。

copy模块提供的deepcopycopy函数能够为任意对象做深复制和浅复制。

In [2]: import copy                                                                                                                    

In [3]: bus1 = Bus(['Alice','Bill','Claire','David'])                                                                                  

In [5]: bus2=copy.copy(bus1)       
                                                                                                    
In [7]: bus3=copy.deepcopy(bus1)                                                                                                       

In [12]: id(bus1),id(bus2),id(bus3)                                                                                                    
Out[12]: (139669803178832, 139669809362000, 139669809504720)

In [13]: bus1.drop("Bill")                                                                                                             

In [14]: bus2.passengers                                                                                                               
Out[14]: ['Alice', 'Claire', 'David']

In [15]: id(bus1.passengers),id(bus2.passengers),id(bus3.passengers)                                                                   
Out[15]: (139669802958592, 139669802958592, 139669809767680)

In [16]: bus3.passengers                                                                                                               
Out[16]: ['Alice', 'Bill', 'Claire', 'David']

(1)这里使用copydeepcopy创建了3个不同的Bus实例。
(2)bus1中的’Bill’下车后,bus2中也没有了
(3)审查passengers属性之后发现,bus1bus2共享同一个列表对象,因为bus2bus1的浅复制副本
(4)bus3是``bus1的深复制副本,因此它的passengers属性指代另外一个列表。

8.4函数的参数作为引用时

Python唯一支持的参数传递模式是"共享传参"(call by sharing)。共享传参指函数的各个形式参数获得实参中各个引用的副本。也就是说,函数内部的形参是实参的别名。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值