文章目录
变量是标注,而不是盒子
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]可变;t1、t2是不同的对象,但是二者值相等。
向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
这里把l1以list类型复制给l2,list(l1)创建了l1的副本。副本l2与原列表l1值相同,但是二者指待了不同的对象。
还可以使用构造方法或者[:]做浅复制(即复制了最外层的容器,副本中的元素是源容器中的元素引用)。

8.3.1为任意对象做深复制和浅复制
深复制(即副本不共享内部对象的引用)。
copy模块提供的deepcopy和copy函数能够为任意对象做深复制和浅复制。
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)这里使用copy和deepcopy创建了3个不同的Bus实例。
(2)bus1中的’Bill’下车后,bus2中也没有了
(3)审查passengers属性之后发现,bus1和bus2共享同一个列表对象,因为bus2是bus1的浅复制副本
(4)bus3是``bus1的深复制副本,因此它的passengers属性指代另外一个列表。
8.4函数的参数作为引用时
Python唯一支持的参数传递模式是"共享传参"(call by sharing)。共享传参指函数的各个形式参数获得实参中各个引用的副本。也就是说,函数内部的形参是实参的别名。
本文详细探讨了Python中变量标识、相等性比较(==与is)、元组的相对不可变性,以及默认浅复制与深复制的区别。通过实例展示了如何使用is和id函数判断对象关系,以及copy和deepcopy在复制列表和复杂对象时的作用。

375

被折叠的 条评论
为什么被折叠?



