Python中如何实现数组更新:创建新数组且原数组保持不变
填字游戏棋盘拷贝后内容同步变更的问题定位与解决
问题背景
开发填字游戏相关功能时,定义了用于表示棋盘方格的Square类:类实例具备x/y坐标、方格字符、标记序号属性,可通过实例的write方法修改方格上的字符内容。开发数组更新逻辑时出现异常行为:尝试基于原方格数组创建新数组、通过write方法在新数组上写入单词时,原数组与新数组的方格内容会发生同步变更。
多轮定位排查过程
- 最初版本直接将原数组赋值给临时变量执行写入操作,未做独立拷贝
- 尝试为
write_word函数添加数组浅拷贝操作后测试,同步修改的问题仍然存在 - 后续改用嵌套列表替代numpy数组存储方格对象,分别实现了基于numpy数组和Python列表版本的棋盘创建、单词写入逻辑,两种实现下问题表现完全一致:创建原始棋盘、对棋盘做拷贝后修改拷贝对象的内容,原始棋盘的内容会同步变化。由此判断问题根源并非数组或列表数据结构本身,而是自定义类对象的引用传递特性
- 测试发现numpy数组与Python列表在浅拷贝场景下会出现相同的同步修改问题,最终在不修改
Square类逻辑的前提下实现了符合预期的功能
相关实现代码
核心方格类定义
class Square: def __init__(self, x,y): self.x = x self.y = y self.letter = " " self.num = 0 def write(self, letter): if self.letter != "!": self.letter = letter.upper() else: print("cannot write on black square") def clear(self): if self.letter != "!": self.letter = " " else: print("cannot write on black square") def black(self): self.letter = "!" def isblack(self): if self.letter == "!": return True else: return False def isfilled(self): if self.letter != "!" and self.letter != " ": return True else: return False def read(self): return self.letter def number(self,num): self.num = num def getnum(self): return self.num def __str__(self): image = " " + self.letter return image def __repr__(self): image = " " + self.letter return image
存在问题的版本实现
- 无独立拷贝的numpy数组写入逻辑
def write_word(crossarray,indexes,word): print("now writing word:",word) temparray = crossarray for i, index in enumerate(indexes): row = int(index // 5) col = int(index % 5) temparray[row,col].write(word[i]) return temparray
- 浅拷贝的numpy数组写入逻辑
def UPDATED_write_word(crossarray,indexes,word): print("now writing word:",word) temparray = crossarray.copy() for i, index in enumerate(indexes): row = int(index // 5) col = int(index % 5) temparray[row,col].write(word[i]) return temparray
- numpy数组版本的棋盘创建逻辑
def create_crossword(down,across,indexes,blacks): cross_array = np.zeros((5,5), dtype=Square) for row in range(len(cross_array)): for col in range(len(cross_array[0])): cross_array[row,col] = Square(row,col) for name, index in enumerate(indexes): row = int(index // 5) col = int(index % 5) cross_array[row,col].number(name+1) for index in blacks: row = int(index // 5) col = int(index % 5) cross_array[row,col].black() return(cross_array)
- Python列表版本的棋盘创建与写入逻辑
def create_crosslist(down,across,indexes,blacks): cross_list = [] for row in range(5): rowlist = [] for col in range(5): rowlist.append(Square(row,col)) cross_list.append(rowlist) for name, index in enumerate(indexes): row = int(index // 5) col = int(index % 5) cross_list[row][col].number(name+1) for index in blacks: row = int(index // 5) col = int(index % 5) cross_list[row][col].black() return cross_list def write_word_list(crosslist,indexes,word): print("now writing word:",word) templist = crosslist for i, index in enumerate(indexes): row = int(index // 5) col = int(index % 5) templist[row][col].write(word[i]) return templist
最终生效的实现
通过copy.deepcopy做深度拷贝,完全断开原对象与拷贝对象的引用关联,无需修改Square类逻辑即可实现棋盘独立修改的效果:
import copy def FINAL_write_word(crossarray,indexes,word): print("now writing word:",word) temparray = copy.deepcopy(crossarray) for i, index in enumerate(indexes): row = int(index // 5) col = int(index % 5) temparray[row,col].write(word[i]) return temparray
内容的提问来源于stack exchange,提问作者Lara Ipek
相关产品推荐
相关产品推荐

