Python 二维列表初始化陷阱:引用共享导致数据异常
本文通过俄罗斯方块开发实例,揭示Python中使用[0]*n创建二维列表时因对象引用共享导致的数据修改异常。详细解析错误原因,并对比直接创建、列表生成式及numpy模块三种正确初始化方法,帮助开发者避免常见陷阱。
背景
近期在深入钻研Python语言,我认为掌握新语言的最佳途径是动手实践一个小项目,因此我决定开发一个简单的俄罗斯方块游戏。在编码过程中,我遇到了一个关于数据结构处理的棘手问题。
def __init__(self, width = 10, height = 30): self.width, self.height = width, height self.board_size = [width, height]
为了记录游戏区域的状态,我采用了一个二维列表结构,其中代表单个格子,数值0表示空格,1表示有方块。显然,游戏初始化阶段需要将所有格子重置为0。查阅Python列表文档后,我发现可以使用这种语法快速生成指定长度的列表,于是顺理成章地写出了如下代码。
self.game_board = [[0] * height] * width
运行代码检查结果,生成的二维列表长宽符合预期,且所有元素均为0,表面看来一切正常,我便继续推进后续逻辑的开发。
问题
然而,在编写方块消除逻辑时出现了意外:测试场景中底部三行全为1,第四行中间为1其余为0,消除操作完成后理论上应剩余一个1下落至第一排。但实际测试显示,所有方块均被错误消除。起初我怀疑是消除算法有误,但在每一步骤中监控的状态后发现,只要某一行中有一个元素被修改为1,整行的所有元素都会同步变为1。
解决
我很快意识到这是由列表对象的引用机制引起的。生成了一个长度为height且内容全为0的列表,由于0是整数(基础数据类型),这种用法本身没有问题。但当对该列表执行操作时,Python并未创建width个独立的列表对象,而是创建了width个指向同一个内部列表的引用。因此,修改其中任意一个子列表,所有子列表都会表现出相同的更改。
查阅Python官方文档后,我将代码修正为:
self.game_board = [([0] * height) for i in range(width)]
重新测试,问题得到解决。
创建二维数组的办法
直接创建法
test = [0, 0, 0], [0, 0, 0], [0, 0, 0]]
这种方法简单直接,但代码冗长繁琐,通常不推荐使用。
列表生成式法
test = [[0 for i in range(m)] for j in range(n)]
熟练掌握列表生成式,对长期编程工作大有裨益。
使用模块numpy创建
import numpy as np test = np.zeros((m, n), dtype=np.int)
总结
这其实是一个基础概念问题,源于对Python中运算符的理解不足,误以为它会对对象进行深拷贝。可能由于前端开发经验较多,对底层数据结构的变化不够敏感,今后仍需加强相关练习。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









