在探讨浅复本和深复本以后,具体来说要介绍python中三个第一类较为==和is
==运算符展开的是第一类的值推论,较为三个第一类的值与否成正比。
is运算符展开的是第一类的身分标记的推论,较为三个第一类的物理地址与否成正比。
None在Python中较为特定,在Python里是个科枫第一类,两个表达式假如是None,它很大和None对准同两个物理地址。None是python中的两个特定的自表达式,则表示两个空的第一类,自表达式是python中的两个特定值。统计数据为空并不代表者是空第一类,比如[],等都并非None。None和任何人第一类较为codice都是False,除他们。
列出两个范例
具体来说 Python 会为 1这个值开辟一块内存,然后表达式 a 和 b 同时指向这块物理地址,即 a 和 b 都是对准 1 这个表达式,因此 a 和 b 的值成正比,id 也成正比,a == b和a is b都返回 True。
不过,需要注意,对于整型数字来说,以上a is b为 True 的结论,只适用于 [-5 , 257 )范围内的数字。
出于对性能优化的考虑,Python 内部会对 -5 到 256 的整型维持两个数组,起到两个缓存的作用。每次创建两个 -5 到 256 范围内的整型数字时,Python 都会从这个数组中返回相对应的值的引用,而并非重新频繁地开辟一块新的内存空间。
上述范例中的 369,Python 则会为三个369开辟两块内存区域,因此 a 和 b 的 ID 不一样,a is b就会返回 False 了。
浅复本shallow copy
浅复本通常的实现方法是使用统计数据类型本身的构造器,此处所说通常情况,是因为后面存有着特定情况(元组的构造器),比如:
当然,Python 中也提供了相对应的函数 copy.copy(),适用于任何人统计数据类型,用来展开浅复本:
前面提到了元组的特定情况,使用 tuple() 或者切片运算符:不会创建一份浅复本,相反,它会返回两个对准相同元组的引用。实际上,使用copy.copy()和copy.deepcopy()得到的,也是两个对准相同元组的引用。
浅复本,是指重新分配一块内存,创建两个新的第一类,里面的元素是原第一类中子第一类的引用。既然复本得到的新的第一类的元素是对原第一类中子第一类的引用,那么有两个点需要注意一下。看一下下面的范例。
这个范例中,我们具体来说初始化了两个列表 l1,里面的元素是两个列表和两个元组;然后对 l1 执行浅复本,赋予 l2。因为浅复本里的元素是对原第一类元素的引用,因此 l2 中的元素和 l1 对准同两个列表和元组第一类。
紧接着,l1.append(100),则表示对 l1 的列表新增元素 100。这个操作不会对 l2 产生任何人影响,因为 l2 和 l1 作为整体是三个不同的第一类,并不共享物理地址。操作过后 l2 不变,l1 会发生改变。
再来看,l1[0].append(3),这里则表示对 l1 中的第两个列表新增元素 3。因为 l2 是 l1 的浅复本,l2 中的第两个元素和 l1 中的第两个元素,共同对准同两个列表,因此 l2 中的第两个列表也会相对应的新增元素 3。操作后 l1 和 l2 都会改变。
最后是l1[1] += (50, 60),因为元组是不可变的,这里则表示对 l1 中的第二个元组拼接,然后重新创建了两个新元组作为 l1 中的第二个元素,而 l2 中没有引用新元组,因此 l2 并不受影响。操作后 l2 不变,l1 发生改变。
上面的范例,对于可变第一类的引用,尤其是列表独享,容易带来一些副作用,要避免这种情况,可以使用深复本。
深复本deep copy
Python 中以 copy.deepcopy() 来实现第一类的深度复本。
可以看到,无论 l1 如何变化,l2 都不变。因为此时的 l1 和 l2 完全独立,没有任何人联系。
深度复本也并非完美的,往往也会带来一系列难题。假如被复本第一类中存有对准自身的引用,那么程序很容易陷入无限循环:
列表 x 中有对准自身的引用,因此 x 是两个无限嵌套的列表。但是我们发现深度复本 x 到 y 后,程序并没有出现 stack overflow 的现象。这是为什么呢?
这是因为深度复本函数 deepcopy 中会维护两个字典,记录已经复本的第一类与其 ID。复本过程中,假如字典里已经存储了将要复本的第一类,则会从字典直接返回,我们来看相对应的源码就能明白:
假如执行如下操作:
出现上面错误的原因很明显,在执行 == 操作时,因为x中存储了自身的引用,会无限的递归与y较为,从而造成RecursionError异常,因为最大递归深度有很大的限制。
假如执行len操作,求x的长度,你会发现
总结
较为运算符==则表示较为第一类间的值与否成正比,而is则表示较为第一类的标记与否成正比,即它们与否对准同两个物理地址。浅复本中的元素,是原第一类中子第一类的引用,因此,假如原第一类中的元素是可变的,改变其也会影响复本后的第一类,存有很大的副作用。深度复本则会递归地复本原第一类中的每两个子第一类,因此复本后的第一类和原第一类互不相关。另外,深度复本中会维护两个字典,记录已经复本的第一类及其 ID,来提高效率并防止无限递归的发生。最后供上一张图方便理解: