Python的秘密生活:Matryoshka陷阱

发布: (2026年1月9日 GMT+8 09:30)
5 min read
原文: Dev.to

Source: Dev.to

Timothy的嵌套计划

Timothy在整理图书馆档案时哼着小曲。他感到无所不能,因为已经掌握了可以克隆列表、避免别名诅咒的 “slice” ([:]) 语法。

“Margaret,我正在重新组织图书馆的分区。我已经制作了书架的主布局图,并在移动任何东西之前做了备份。看看这个漂亮的浅拷贝。”

他展示了自己的代码,对新学到的技巧充满信心。

# Timothy's Nested Plan
shelf_A = ["Fiction", "Mystery"]
shelf_B = ["Biography", "History"]

# The Master Layout: a list of lists
library_layout = [shelf_A, shelf_B]

# The Backup: using the slice trick
backup_layout = library_layout[:]  # (Surely this clones everything... right?)

# Move "Mystery" to a new location in the backup
backup_layout[0].remove("Mystery")

print(f"Backup Shelf A: {backup_layout[0]}")
print(f"Master Shelf A: {library_layout[0]}")

控制台输出

Backup Shelf A: ['Fiction']
Master Shelf A: ['Fiction']

Timothy大为震惊。原本以为 slice 能创建完全独立的列表,然而 “Mystery” 这本书也从主布局中消失了。

浅拷贝陷阱

Margaret 解释说,切片(或 list.copy())会创建一个 新的外层容器,但 共享对内部可变对象的引用

Original List        Backup List
   [Box 1]             [Box 2]
      |                   |
      +-------+   +-------+
              |   |
              v   v
           [Inner List]
          (Shared Data!)

当 Timothy 修改 backup_layout[0] 时,实际上是修改了 library_layout[0] 所指向的同一个内部列表。这就是经典的 Matryoshka(套娃)陷阱:浅拷贝只复制了第一层。

深拷贝解决方案

为了获得一个完全独立的副本——包括所有嵌套的可变对象——Timothy 需要进行 深拷贝

import copy

shelf_A = ["Fiction", "Mystery"]
shelf_B = ["Biography", "History"]
library_layout = [shelf_A, shelf_B]

# Deep copy: recursively copies everything
backup_layout = copy.deepcopy(library_layout)

backup_layout[0].remove("Mystery")

print(f"Backup Shelf A: {backup_layout[0]}")
print(f"Master Shelf A: {library_layout[0]}")

输出

Backup Shelf A: ['Fiction']
Master Shelf A: ['Fiction', 'Mystery']

copy.deepcopy() 会遍历整个对象图,克隆它遇到的每个可变元素,因此对备份的修改永远不会影响原始对象。

性能考虑

  • 为什么深拷贝不是默认?
    深拷贝在时间和内存上都昂贵。对于小的或扁平的结构,开销可以忽略不计,但反复克隆大型、深度嵌套的结构会成为性能瓶颈。

  • Python 的默认哲学
    Python 追求速度;浅拷贝成本低,足以满足许多使用场景。当需要对嵌套数据的安全性时,必须显式请求。

复制指南

情形推荐的复制方式
扁平列表(例如 [1, 2, 3]浅拷贝(list[:]list.copy()copy.copy(x)
嵌套列表/字典(例如 [[1, 2], [3, 4]]深拷贝(copy.deepcopy(x)
需要对任意对象进行通用浅拷贝copy.copy(x)(适用于列表、字典、自定义对象)
需要对复杂结构进行完全独立的克隆copy.deepcopy(x)

快速参考

import copy

# 浅拷贝(仅第一层)
shallow = original_list[:]          # 或 original_list.copy()
shallow_generic = copy.copy(original)  # 适用于任意对象

# 深拷贝(完整递归)
deep = copy.deepcopy(original)

何时使用深拷贝

  • 嵌套可变容器 – 列表的列表、包含列表的字典、对象中包含其他可变对象。
  • 需要隔离 – 当对副本的修改绝不能影响原始对象,即使是在结构的深层也是如此。
  • 测试或沙盒 – 为实验创建安全的沙盒版数据。

对于简单、扁平的数据结构,浅拷贝仍是最有效的选择。


故事在下一集 “The Loophole” 中继续,那里在迭代列表时修改列表会导致时间线混乱、跳过。

Back to Blog

相关文章

阅读更多 »

第10题:去重

问题描述:我们需要一个函数,从列表中删除重复项,同时保留元素的原始顺序。例如 remove_duplicates(1, 2, 2, 3)。

Python的秘密生活:漏洞

在遍历列表时修改列表的陷阱以及如何修复它。Timothy 正盯着屏幕上的传感器数据列表。> “我叫它删除 e...”