小能豆

Python 复制列表问题

py

我不知道这里出了什么问题,但我确信这里有人可以提供帮助。我有一个列表mylst(列表的列表),它被复制并传递到方法中foofoo遍历列表并用传入的变量替换行中的第一个元素并返回更改后的列表。我打印列表,看到它给了我期望的结果。我用另一个副本mylst和不同的传入变量再次重复该过程。所以两个返回的列表应该不同;但是当我再次检查第一个列表时,我发现它现在是第二个列表,也mylst已更改为第二个列表。我复制列表的方式不正确吗?我正在用mylst[:]方法复制它。另一个有趣的观察是所有列表 ID 都不同。这难道不意味着它与其他列表不同吗?这是我的问题的一个例子。

def printer(lst):
    print "--------------"
    for x in lst:
        print x
    print "--------------\n"

def foo(lst, string):

    for x in lst:
        x[0] = string

    print "in foo"
    for x in lst:
        print x
    print "in foo\n"

    return lst

mylst = [[1, 2, 3], [4, 5, 6]]
print "mylst", id(mylst), "\n"

first = foo(mylst[:], "first")
print "first", id(first)
printer(first) # Correct

second = foo(mylst[:], "second")
print "second", id(second)
printer(second) # Correct

print "first", id(first)
printer(first) # Wrong

print "mylst", id(mylst)
printer(mylst) # Wrong

这是我电脑上的打印件

mylst 3076930092 

in foo
['first', 2, 3]
['first', 5, 6]
in foo

first 3076930060
--------------
['first', 2, 3]
['first', 5, 6]
--------------

in foo
['second', 2, 3]
['second', 5, 6]
in foo

second 3076929996
--------------
['second', 2, 3]
['second', 5, 6]
--------------

first 3076930060
--------------
['second', 2, 3]
['second', 5, 6]
--------------

mylst 3076930092
--------------
['second', 2, 3]
['second', 5, 6]
--------------

阅读 27

收藏
2024-11-19

共1个答案

小能豆

问题出在您使用 mylst[:] 创建副本的方式上。mylst[:] 会执行 浅拷贝,即它创建了一个新的外层列表对象,但内层列表(mylst 中的行)仍然是原始内层列表的引用。

关键点解释:

  1. 浅拷贝的行为
  2. mylst[:] 创建了一个新的外层列表,但内部的子列表仍指向原始 mylst 的内存地址。
  3. 因此,当 foo 修改列表中的行时,它会影响到原始 mylst 和其他副本,因为它们共享相同的内层列表引用。

  4. 为什么列表 ID 不同

  5. 外层列表(mylstfirstsecond)的 ID 是不同的,因为每次 mylst[:] 都创建了一个新的外层列表。
  6. 但内层列表(如 [1, 2, 3][4, 5, 6])的 ID 是相同的,因为它们在所有副本中共享。

修复方法:

为了避免这个问题,您需要创建 深拷贝,即不仅复制外层列表,还复制其内层列表。以下是两种解决方案:


方法 1:使用 copy 模块进行深拷贝

copy.deepcopy 会递归复制所有层级的数据结构。

import copy

def foo(lst, string):
    for x in lst:
        x[0] = string
    return lst

mylst = [[1, 2, 3], [4, 5, 6]]

first = foo(copy.deepcopy(mylst), "first")
print("first")
for x in first:
    print(x)

second = foo(copy.deepcopy(mylst), "second")
print("second")
for x in second:
    print(x)

print("mylst")
for x in mylst:
    print(x)

输出:

first
['first', 2, 3]
['first', 5, 6]
second
['second', 2, 3]
['second', 5, 6]
mylst
[1, 2, 3]
[4, 5, 6]

方法 2:手动创建深拷贝

如果不想使用 copy.deepcopy,可以通过列表推导式手动创建新的内层列表:

mylst_copy = [row[:] for row in mylst]

在您的代码中:

first = foo([row[:] for row in mylst], "first")
second = foo([row[:] for row in mylst], "second")

输出结果与使用 copy.deepcopy 相同。


为什么浅拷贝会导致这个问题?

这是因为 Python 的列表存储的是对其元素的引用,而不是实际的数据副本。例如:

mylst = [[1, 2, 3], [4, 5, 6]]
shallow_copy = mylst[:]

# 修改 shallow_copy 的第一个内层列表
shallow_copy[0][0] = "modified"

# 这也会影响到 mylst
print(mylst)  # [['modified', 2, 3], [4, 5, 6]]

深拷贝可以确保新列表中的所有对象都是独立的副本。


总结:

  • 浅拷贝mylst[:]):只复制外层列表,内层列表仍共享引用。
  • 深拷贝copy.deepcopy 或手动 [row[:] for row in mylst]):复制外层和内层列表,确保它们完全独立。
  • 在需要修改嵌套结构的情况下,优先选择深拷贝以避免数据污染。
2024-11-19