Python - 在浅复制的情况下,在列表中追加和更改列表中的元素有何不同

问题描述 投票:0回答:2

好吧,所以我对列表中的追加和更改操作相对于浅复制的区别有疑问。

以下是输出:

***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]]
python-3.x list append shallow-copy
2个回答
0
投票

直接OUTA Python 文档:

  • 浅拷贝构造一个新的复合对象,然后(到 尽可能)将引用插入到其中找到的对象 原来的。

  • 深拷贝构造一个新的复合对象,然后, 递归地,将在其中找到的对象的副本插入其中 原创。

有关更多信息,请参阅此处:https://realpython.com/copying-python-objects/


0
投票

如果您认为副本分为三种而不是两种[1]:

,则可以消除这种混乱
  1. 参考副本
  2. 浅复制
  3. 深拷贝

虽然参考副本在技术上并不是副本,但也可以这么认为。

考虑以下四个列表:

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
都指向相同的内存位置。这个等式的另一端是深度复制,它是完全不同的内存地址上的完全不同的变量,并且与原始版本无关。

我们需要记住的是两者之间的区别。现在在一些非常具体的数据结构中,如上面的“嵌套列表”和类似的分层数据结构(如“字典”)中所述,其中可变数据集中有可变数据集。还存在第三种复制的可能性。第三种复制是浅复制,位于两个极端的中间,参考复制和深层复制。 这里,外部数据列表是按值传递的,意味着深度复制,内部列表是按引用传递的,意味着引用复制。因此,外部列表具有类似深度复制的行为,而内部列表具有类似引用复制的行为。 附录

这个问题很重要,应该引起重视。答案需要理解一些相关概念,包括:

浅复制与深复制(上面已经解释过)

不可变数据类型与可变数据类型
  1. 按值传递与按引用传递
  2. 可变与不可变?
  3. 首先,在Python中,只有四种内置类型是可变的:列表、字典、集合和字节数组。其余类型:int、float、complex、str、tuple、bytes、bool、frozenset 都是不可变的

[2]

. 考虑下面的例子: 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'] 所以它是参考副本。

另一种机制是按值传递,它是按引用传递的另一个极端,类似于深度复制,仅复制值,与原始数据集不保持任何关系。

© www.soinside.com 2019 - 2024. All rights reserved.