>>> a = [1, 2, 3] >>> b = a >>> a.append(4) >>> b [1, 2, 3, 4]
如果把变量想象为盒子,那么无法解释 Python 中的赋值;应该把变量视作便利贴,这样示例中的行为就好解释了
注意: 对引用式变量来说,说把变量分配给对象更合理,反过来说就有问题。毕竟,对象在赋值之前就创建了 标识、相等性和别名Lewis Carroll 是 Charles Lutwidge Dodgson 教授的笔名。Carroll 先生指的就是 Dodgson 教授,二者是同一个人。? 用 Python 表达了这个概念。
charles 和 lewis 指代同一个对象>>> lewis = charles
>>> lewis is charles
True
>>> id(lewis), id(charles)
(4303312648, 4303312648)
>>> lewis['balance'] = 950
>>> charles
{'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
charles 和 lewis 绑定同一个对象,alex 绑定另一个具有相同内容的对象
alex 与 charles 比较的结果是相等,但 alex 不是charles>>> lewis
{'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
>>> alex = {'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
>>> lewis == alex
True
>>> alex is not lewis
True
在==和is之间选择
== 运算符比较两个对象的值(对象中保存的数据),而 is 比较对象的标识。通常,我们关注的是值,而不是标识,因此 Python 代码中 == 出现的频率比 is 高。然而,在变量和单例值之间比较时,应该使用 is。目前,最常使用 is检查变量绑定的值是不是 None。下面是推荐的写法:x is None
x is not None
>>> t1 = (1, 2, [30, 40]) >>> t2 = (1, 2, [30, 40]) >>> t1 == t2 True >>> id(t1[-1]) 4316861320 >>> t1[-1].append(1000) >>> t1 (1, 2, [30, 40, 1000]) >>> t1 == t2 False
>>> l1 = [3, [55, 44], (7, 8, 9)] >>> l2 = list(l1) >>> l3 = l1[:] >>> l2 [3, [55, 44], (7, 8, 9)] >>> l3 [3, [55, 44], (7, 8, 9)] >>> l1 == l2 == l3 True >>> l2 is l1 False >>> l3 is l1 False
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1) #浅复制了l1
l1.append(100) #l1列表在尾部添加数值100
l1[1].remove(55) #移除列表中第1个索引的值
print('l1:', l1)
print('l2:', l2)
l2[1] += [33, 22] #l2列表中第1个索引做列表拼接
l2[2] += (10, 11) #l2列表中的第2个索引做元祖拼接
print('l1:', l1)
print('l2:', l2)
为任意对象做深复制和浅复制
浅复制没什么问题,但有时我们需要的是深复制(即副本不共享内部对象的引用)。copy 模块提供的 deepcopy 和 copy 函数能为任意对象做深复制和浅复制。
? 校车乘客在途中上车和下车
1 class Bus: 2 3 def __init__(self, passengers=None): 4 if passengers is None: 5 self.passengers = [] 6 else: 7 self.passengers = list(passengers) 8 9 def pick(self, name): 10 self.passengers.append(name) 11 12 def drop(self, name): 13 self.passengers.remove(name)
1 from copy import copy, deepcopy
2
3 bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
4 bus2 = copy(bus1) #bus2浅复制的bus1
5 bus3 = deepcopy(bus1) #bus3深复制了bus1
6 print(id(bus1), id(bus2), id(bus3)) #查看三个对象的内存地址
7
8 bus1.drop('Bill') #bus1的车上Bill下车了
9 print('bus2:', bus2.passengers) #wtf....bus2中的Bill也没有了,见鬼了!
10 print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)) #审查 passengers 属性后发现,bus1和bus2共享同一个列表对象,因为 bus2 是 bus1 的浅复制副本
11
12 print('bus3:', bus3.passengers) #bus3是bus1 的深复制副本,因此它的 passengers 属性指代另一个列表
4324829840 4324830176 4324830736 bus2: ['Alice', 'Claire', 'David'] 4324861256 4324861256 4324849608 bus3: ['Alice', 'Bill', 'Claire', 'David']
>>> a = [10, 20] >>> b = [a, 30] >>> a.append(b) >>> a [10, 20, [[...], 30]] >>> from copy import deepcopy >>> c = deepcopy(a) >>> c [10, 20, [[...], 30]]
>>> def f(a, b): ... a += b ... return a ... >>> x = 1 >>> y = 2 >>> f(x, y) 3 >>> x, y (1, 2) >>> a = [1, 2] >>> b = [3, 4] >>> f(a, b) [1, 2, 3, 4] >>> a, b ([1, 2, 3, 4], [3, 4]) >>> t = (10, 20) >>> u = (30, 40) >>> f(t, u) (10, 20, 30, 40) >>> t, u ((10, 20), (30, 40))
1 class HauntedBus:
2 '''
3 备受折磨的幽灵车
4 '''
5
6 def __init__(self, passengers=[]):
7 self.passengers = passengers
8
9 def pick(self, name):
10 self.passengers.append(name)
11
12 def drop(self, name):
13 self.passengers.remove(name)
14
15
16 bus1 = HauntedBus(['Alice', 'Bill'])
17 print('bus1上的乘客:', bus1.passengers)
18 bus1.pick('Charlie') #bus1上来一名乘客Charile
19 bus1.drop('Alice') #bus1下去一名乘客Alice
20 print('bus1上的乘客:', bus1.passengers) #打印bus1上的乘客
21
22 bus2 = HauntedBus() #实例化bus2
23 bus2.pick('Carrie') #bus2上来一名课程Carrie
24 print('bus2上的乘客:', bus2.passengers)
25
26 bus3 = HauntedBus()
27 print('bus3上的乘客:', bus3.passengers)
28 bus3.pick('Dave')
29 print('bus2上的乘客:', bus2.passengers) #登录到bus3上的乘客Dava跑到了bus2上面
30
31 print('bus2是否为bus3的对象:', bus2.passengers is bus3.passengers)
32 print('bus1上的乘客:', bus1.passengers)
bus1上的乘客: ['Alice', 'Bill'] bus1上的乘客: ['Bill', 'Charlie'] bus2上的乘客: ['Carrie'] bus3上的乘客: ['Carrie'] bus2上的乘客: ['Carrie', 'Dave'] bus2是否为bus3的对象: True bus1上的乘客: ['Bill', 'Charlie']
1 class TwilightBus:
2 """让乘客销声匿迹的校车"""
3
4 def __init__(self, passengers=None):
5 if passengers is None:
6 self.passengers = passengers
7 else:
8 self.passengers = passengers #这个地方就需要注意了,这里传递的是引用的别名
9
10 def pick(self, name):
11 self.passengers.append(name) #会修改构造放的列表,也就是会修改外部的数据
12
13 def drop(self, name):
14 self.passengers.remove(name) #会修改构造放的列表,也就是会修改外部的数据
15
16 basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
17 bus = TwilightBus(basketball_team)
18 bus.drop('Tina') #bus中乘客Tina下去了
19 bus.drop('Pat') #bus中课程Pat下去了
20
21 print(basketball_team) #wtf....为毛线的basketball的里面这两个人也木有了~~MMP
['Sue', 'Maya', 'Diana']
>>> a = [1, 2, 3] >>> b = a >>> c = list(a) >>> b.append(10) >>> a [1, 2, 3, 10] >>> b [1, 2, 3, 10] >>> c [1, 2, 3]
>>> import weakref
>>> s1 = {1, 2, 3}
>>> s2 = s1 #s1和s2是别名,指向同一个集合
>>> def bye(): #这个函数一定不能是要销毁的对象的绑定方法,否则会有一个指向对象的引用
... print('Gone with the wind...')
...
>>> ender = weakref.finalize(s1, bye) #在s1引用的对象上注册bye回调
>>> ender.alive #调用finalize对象之前,.alive属性的值为True
True
>>> del s1 #del不删除对象,而是删除对象的引用
>>> ender.alive
True
>>> s2 = 'spam' #重新绑定最后一个引用s2,让{1, 2, 3}无法获取,对象呗销毁了,调用bye回调,ender.alive的值编程了False
Gone with the wind...
>>> ender.alive
False
>>> import weakref
>>> a_set = {0, 1}
>>> wref = weakref.ref(a_set) #创建弱引用对象wref,下一行审查它
>>> wref
<weakref at 0x101ce03b8; to 'set' at 0x101cd8d68>
>>> 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