☰
默认游戏配置也跟着变了:Python 浅拷贝没有复制内层列表
2026/10/12 3:54:53 网站建设 项目流程

大家好,我是 SiKi老师。写游戏设置工具时,常见做法是从一份默认配置生成玩家配置,再修改按键。如果修改后的默认值也变了,先检查两份字典是否共享了内层列表,不要急着怀疑保存文件的代码。

本文在 Windows、Python 3.12.14 中运行自拟的内存数据,不读写真实游戏配置。依据是 Python 3.12 copy 文档。示例只讨论普通字典、列表和整数,不测试 Unity、Godot 的资源复制,也不涉及引擎对象生命周期。

一 字典变成两份,列表可能仍是一份

defaults={"volume":50,"keys":["W","S"]}player=defaults.copy()player["volume"]=10player["keys"].append("Space")print(defaults)

实际输出{'volume': 50, 'keys': ['W', 'S', 'Space']}。顶层volume仍是 50,但默认按键列表多了Space。为什么两个操作的影响不同?dict.copy()做的是浅拷贝:新建外层字典,内层成员仍引用原来的对象。

给player["volume"]赋值,是替换玩家字典里的这个成员;向player["keys"]追加元素,是修改那份共享列表。不能只看“外层已经 copy 了”,就推断整个数据树都相互独立。

这个小例子也说明,故障不需要等到保存文件才发生。在内存里打印默认值,已经能看见变化。先缩小到复制和修改这两步,能避免把问题混进文件路径、编码和加载流程。

二 检查身份,不只比较内容

在前面的代码后面加两行:

print(playerisdefaults)print(player["keys"]isdefaults["keys"])

实测分别是False、True:字典不是同一个对象,按键列表却是同一个对象。这里用is检查对象身份,而不是用==判断内容是否相等。

两份不同的列表可以内容相等;同一份列表当然也可以暂时没有可见差异。因此,只打印两边内容一样,并不能证明复制失败或成功。判断共享关系时,需要把“值是否相同”和“对象是否相同”分开。

如果写的是player = defaults,则连外层字典也没有复制。赋值只是让另一个名字指向同一个对象。这个情况与浅拷贝不同,排查时不要都统称为“copy 没生效”。

三 对普通嵌套数据尝试深拷贝

重新创建默认数据,再运行下面的完整例子;不要沿用前面已经被追加过的defaults。

fromcopyimportdeepcopy defaults={"volume":50,"keys":["W","S"]}player=deepcopy(defaults)player["volume"]=10player["keys"].append("Space")print(defaults)print(player)print(player["keys"]isdefaults["keys"])

我得到的输出是:

{'volume': 50, 'keys': ['W', 'S']} {'volume': 10, 'keys': ['W', 'S', 'Space']} False

这组普通嵌套数据里,玩家按键列表被修改后,默认列表保持原状。验证重点不是函数名字听起来更完整,而是修改副本后,原始数据是否仍符合预期,以及需要分离的列表是否真的分离。

deepcopy()不是让每个对象都无条件变成全新的对象。它有已复制对象的记录机制,自定义类型也可以规定复制行为;某些类型不能按普通配置数据的方式复制。本文的验证范围不包含文件、套接字、引擎资源或自定义对象。

四 先规定哪些数据应该共享

场景需要确认的约定
玩家修改自己的按键默认按键不应随之改变
全局只读说明表是否允许各配置共享,如何避免写入
大型资源或运行时对象是否具有专门的创建、引用或复制机制

并不是所有共享引用都是错误。只读数据可能刻意共享;把大型结构全部复制,可能付出额外成本,也可能破坏原本需要共享的关系。因此,不建议看到嵌套结构就批量替换成深拷贝。

如果只需要复制一个按键列表,也可以针对该成员设计复制方案,但应检查其中是否还含有可变对象。复制一层列表并不自动解决更深层的共享。方案应依据实际结构,而不是靠“多加一次 copy”碰运气。

五 用修改副本的测试守住默认值

我会给配置工具加一个小测试:新建默认数据,生成玩家副本,修改玩家按键,然后断言默认列表仍然只有W、S。以后添加按键分组等嵌套结构时,再调整测试覆盖这些新成员。

仅断言复制后的内容相等不够,因为还没进行任何修改;仅断言外层身份不同也不够,因为内部仍可能共享。测试应围绕业务上“不该一起变”的字段设计。

本文只确认了小型内存示例的行为,没有测性能、线程并发或存档迁移,不覆盖真正的游戏输入系统。你需要的是独立的玩家设置,还是几份共同读取的只读模板?先把这个约定说清,再选择复制策略。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询