为什么针对简单列表,浅拷贝的行为表现得像深拷贝

3

我将翻译有关Python浅拷贝和深拷贝概念的内容。我注意到大部分文章/博客/回答都是使用嵌套列表进行解释。

import copy
lst = [[1,2,3],[4,5,6]]
b = copy.copy(lst)
c = copy.deepcopy(lst)

# Shallow copy demo
b[0][0] = 9
print(b)
# >>> [[9, 2, 3], [4, 5, 6]]
print(lst)    
# >>> [[9, 2, 3], [4, 5, 6]]

# Deepcopy demo
c[0][0] = 10
print(c)
# >>> [[10, 2, 3], [4, 5, 6]] 
print(lst)
# >>> [[9, 2, 3], [4, 5, 6]]

通过上述简单示例,我理解了浅拷贝和深拷贝的概念。但是当我在一个简单列表(一维列表)上实现这个概念时,观察结果是浅拷贝的行为类似于深拷贝。

import copy
lst = [1,2,3]
b = copy.copy(lst)
c = copy.deepcopy(lst)

# Shallow copy demo
b[0] = 0
print(b)
# >>> [0, 2, 3]
print(lst)
# >>> [1,2,3]

# Deepcopy demo
c[0] = 9
print(c)
# >>> [9,2,3]
print(lst)
# >>> [1,2,3]

这表明copy.copy(lst)表现不同,并执行深拷贝而非浅拷贝。

我想了解为什么copy.copy()在嵌套列表和简单列表上的行为不同。此外,如果我要使浅表副本适用于简单列表,该怎么做?


1
只有在数据结构存在多个层级时,“浅层”和“深层”才有意义。但是对于您的整数列表来说,情况并非如此。 - jasonharper
@jasonharper,谢谢您让它更清晰明了。 - TheLazy
1个回答

3
你得到的结果与“深度水平”没有直接关系,这里最重要的是要牢记“可变性”的概念。
列表是可变的,而数字值不可变。这意味着你可以添加或修改列表中的项,但这些操作不会创建或销毁列表,它们只会改变它。你可以使用内置函数 id() 来验证这一点,它会给出一个变量的内存地址:
lst = [1, 2, 3]
print(id(lst)) # the number printed by this...
lst.append(4)
lst[1] = 0
print(id(lst)) # should be the same printed by this one. That tells us that 
               # the variable 'lst' keeps referecing the same object, although
               # the object have changed in form (mutated)

数字是完全不同的,这是有意义的,因为数值类型的变量只能存储单个数值:

a = 5
print(id(a)) # the number printed by this...
a = 6
print(id(a)) # should be different than this one, meaning that a new numeric 
             # value were created and stored in a different memory address

在线

b[0][0] = 9

在你的第一个例子中,列表b [0]被操作,但它仍然是相同的对象,由于b是浅拷贝,b [0]只是对lst [0]中相同列表的引用,因此当我们打印lst时,我们会看到它也发生了改变。
在你的实现中,当你赋值b [0] = 0时,Python创建值0,将其存储在新的内存位置,并覆盖b [0]lst [0]的引用(因为这是数字类型的自然行为)。
正如所说的那样,这与复合数据结构的嵌套级别无关,因为其中一些是不可变的(例如元组),并且与可变数据结构发生的情况相同。
你可以在这里了解有关id()内置函数的更多信息,以及这里了解有关可变和不可变类型的更多信息。
希望这个答案能帮助你!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接