好吧,所以我对列表中的追加和更改操作相对于浅复制的区别有疑问。
以下是输出:
***After Shallow Copy***
a = [[1, 2, 3], ['a', 'b', 'c'], [True, False]]
b = [[1, 2, 3], ['a', 'b', 'c'], [True, False]]
***After appending in list a (Here appending in list a doesn't affect list b)***
a = [[1, 2, 3], ['a', 'b', 'c'], [True, False], [1, 'a', True]]
b = [[1, 2, 3], ['a', 'b', 'c'], [True, False]]
***After appending in list b (Same as above appending in b doesn't affect a)***
a = [[1, 2, 3], ['a', 'b', 'c'], [True, False], [1, 'a', True]]
b = [[1, 2, 3], ['a', 'b', 'c'], [True, False], [2, 'b', False]]
***After changing 2 to 10 in list a (But changing an element gets reflected in both)***
a = [[1, 10, 3], ['a', 'b', 'c'], [True, False], [1, 'a', True]]
b = [[1, 10, 3], ['a', 'b', 'c'], [True, False], [2, 'b', False]]
***After changing a to z in list b (Same as above changed an element in b and reflected in both)***
a = [[1, 10, 3], ['z', 'b', 'c'], [True, False], [1, 'a', True]]
b = [[1, 10, 3], ['z', 'b', 'c'], [True, False], [2, 'b', False]]
直接OUTA Python 文档:
浅拷贝构造一个新的复合对象,然后(到 尽可能)将引用插入到其中找到的对象 原来的。
深拷贝构造一个新的复合对象,然后, 递归地,将在其中找到的对象的副本插入其中 原创。
有关更多信息,请参阅此处:https://realpython.com/copying-python-objects/
如果您认为副本分为三种而不是两种[1]:
,则可以消除这种混乱虽然参考副本在技术上并不是副本,但也可以这么认为。
考虑以下四个列表:
import copy
original = [['a'], ['b'], ['c']] # original
ref = original # reference copy
shallow = list(original) # shallow copy
deep = copy.deepcopy(original) # deep copy
print('original =', original)
print('ref =', ref)
print('shallow =', shallow)
print('deep =', deep)
original = [['a'], ['b'], ['c']]
ref = [['a'], ['b'], ['c']]
shallow = [['a'], ['b'], ['c']]
deep = [['a'], ['b'], ['c']]
现在让我们改变原来的
original.append('d')
print('original =', original)
print('ref =', ref)
print('shallow =', shallow)
print('deep =', deep)
original = [['a'], ['b'], ['c'], 'd']
ref = [['a'], ['b'], ['c'], 'd']
shallow = [['a'], ['b'], ['c']]
deep = [['a'], ['b'], ['c']]
现在让我们只更改原始元素的第一个元素
original[0][0] = 'X'
print('original =', original)
print('ref =', ref)
print('shallow =', shallow)
print('deep =', deep)
original = [['X'], ['b'], ['c'], 'd']
ref = [['X'], ['b'], ['c'], 'd']
shallow = [['X'], ['b'], ['c']]
deep = [['a'], ['b'], ['c']]
如您所见,唯一复制所有更改(无论是追加还是更新)的是引用副本,因为它根本不是副本。
original
和ref
都指向相同的内存位置。这个等式的另一端是深度复制,它是完全不同的内存地址上的完全不同的变量,并且与原始版本无关。
我们需要记住的是两者之间的区别。现在在一些非常具体的数据结构中,如上面的“嵌套列表”和类似的分层数据结构(如“字典”)中所述,其中可变数据集中有可变数据集。还存在第三种复制的可能性。第三种复制是浅复制,位于两个极端的中间,参考复制和深层复制。 这里,外部数据列表是按值传递的,意味着深度复制,内部列表是按引用传递的,意味着引用复制。因此,外部列表具有类似深度复制的行为,而内部列表具有类似引用复制的行为。 附录
这个问题很重要,应该引起重视。答案需要理解一些相关概念,包括:
不可变数据类型与可变数据类型
.
考虑下面的例子:
var_1 = 10
print(var_1, id(var_1))
var_1 += 1
print(var_1, id(var_1))
var_1
是整数类型,因此它是不可变的。我们为其分配值 10,然后使用
id
函数检查其内存位置。同一存储位置中的任何内容都表示相同的事物,不同的存储位置表示不同的事物。我们只需将 1 添加到 var_1 即可。加 1 后,我们再次检查内存位置。
10 136529258070544
11 136529258070576
事实证明,加1后,它不再位于同一内存位置,这意味着它不是同一个var_1,它是位于不同位置的完全不同的变量。虽然它具有相同的名称,但它不再是相同的变量。这是因为整数是不可变的,它们不能改变。我们有一种错觉,认为 var_1 已经改变,但实际上它没有改变,它被完全不同位置上的完全不同的变量替换。
现在,让我们再次执行相同的操作,但使用不同的数据类型。
考虑下面这个例子:
var_2 = ['a', 'b', 'c']
print(var_2, id(var_2))
var_2 += 'd'
print(var_2, id(var_2))
现在 var_2 是一个列表,这意味着它是可变的,这意味着它应该在不改变其在内存中的位置的情况下进行更改。让我们看看输出:
['a', 'b', 'c'] 136528150852544
['a', 'b', 'c', 'd'] 136528150852544
如您所见,列表已更改,但其在计算机上的地址(位置)没有更改。那是因为列表是可变的。
按引用传递与按值传递
正如您在上面的第一个示例中看到的,唯一复制所有更改(无论是追加还是更新)的是引用副本,因为它根本不是副本。
original
ref
都指向相同的内存位置。而这种向函数传递数据的机制就叫做:pass-by-reference。在 python 中,函数的所有参数都是通过引用传递的。
考虑下面的例子:def function(b):
b.append('d')
return b
a = ['a', 'b', 'c']
print('a before function', a)
function(a)
print('a after function', a)
上面的输出可以告诉你当a传递给b时,你是在进行
引用复制、浅复制
还是深复制。输出是:
a before function ['a', 'b', 'c']
a after function ['a', 'b', 'c', 'd']
所以它是参考副本。
另一种机制是按值传递,它是按引用传递的另一个极端,类似于深度复制,仅复制值,与原始数据集不保持任何关系。