Python OOP开发国际象棋:如何避免move对象属性被数组操作修改?
问题描述
我正在做学校项目,开发一款国际象棋程序,目前先实现兵的移动逻辑。遇到的问题是:修改棋盘二维数组self.positions中对应位置的对象属性时,传入makeMove方法的move对象(包含被移动棋子、起始/结束位置等属性)的属性也跟着变了。标记为thisline的两次print输出不一样,能证明这点。我试过temp = move复制move对象,但没用。self属于GameState类,我不想让move对象的属性被修改,因为后续undoMove函数要依赖它,避免棋子的moved标记等属性变化导致逻辑问题。
相关代码
makeMove方法
def makeMove(self, move): print(move.pieceMoved.row, move.pieceMoved.col, move.pieceMoved.moved) # thisline self.positions[move.initialRow][move.initialCol] = "00" self.positions[move.endRow][move.endCol] = move.pieceMoved self.positions[move.endRow][move.endCol].row = move.endRow # 更新被移动棋子的位置 self.positions[move.endRow][move.endCol].col = move.endCol # 如果是兵,修改初始移动标记(后续王和车可能也会用到类似逻辑) if isinstance(move.pieceMoved, Pawn): self.positions[move.endRow][move.endCol].moved = True self.move_log.append(move) # 记录移动,方便后续撤销 print(move.pieceMoved.row, move.pieceMoved.col, move.pieceMoved.moved) # this line self.white_turn = not self.white_turn # 切换回合
GameState类
class GameState(): def __init__(self): # 棋盘是8x8的二维列表,每个元素是两个字符 # 第一个字符代表颜色:"b"黑,"w"白 # 第二个字符代表棋子类型 # "00"表示空位置 self.positions = [ ['bR', 'bN', 'bB', 'bQ', 'bK', 'bB', 'bN', 'bR'], ['bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP'], ['00', '00', '00', '00', '00', '00', '00', '00'], ['00', '00', '00', '00', '00', '00', '00', '00'], ['00', '00', '00', '00', '00', '00', '00', '00'], ['00', '00', '00', '00', '00', '00', '00', '00'], ['wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP'], ['wR', 'wN', 'wB', 'wQ', 'wK', 'wB', 'wN', 'wR'], ] self.white_turn = True self.move_log = [] piece_construct = {"R" : Rook, "N" : Knight, "B" : Bishop, "Q" : Queen, "K" : King, "P" : Pawn, } for row in range(len(self.positions)): for col in range(len(self.positions[row])): if self.positions[row][col] != "00": self.positions[row][col] = piece_construct[self.positions[row][col][1]](self.positions[row][col], (row,col))
Piece和Pawn类
class Piece(): def __init__(self, label, square): self.colour = label[0] self.title = label self.row = square[0] self.col = square[1] class Pawn(Piece): def __init__(self, label, square): super().__init__(label, square) self.moved = False
问题原因
Python中对象是引用传递:
self.positions[move.endRow][move.endCol] = move.pieceMoved只是把棋子对象的引用存到了棋盘数组里,数组里的对象和move.pieceMoved指向的是同一个实例。- 之后修改数组里对象的
row、col、moved属性,本质就是修改move.pieceMoved这个对象的属性,所以两次print的结果不一样。 temp = move只是复制了move对象的引用,并没有创建新的move实例,原对象还是会被修改。
解决方案
核心是复制棋子对象,让棋盘里的棋子和move里的棋子是两个独立实例,修改棋盘里的棋子不会影响原move对象。
步骤1:给Piece类添加复制方法
在Piece类里新增一个copy方法,用来创建属性完全相同的新实例:
class Piece(): def __init__(self, label, square): self.colour = label[0] self.title = label self.row = square[0] self.col = square[1] def copy(self): # 创建当前棋子的副本 return Piece(self.title, (self.row, self.col)) class Pawn(Piece): def __init__(self, label, square): super().__init__(label, square) self.moved = False def copy(self): # 重写Pawn的copy方法,保留moved属性 pawn_copy = Pawn(self.title, (self.row, self.col)) pawn_copy.moved = self.moved return pawn_copy
步骤2:修改makeMove方法,使用棋子副本
在makeMove里,创建棋子的副本,把副本放到棋盘数组中,修改副本的属性:
def makeMove(self, move): print(move.pieceMoved.row, move.pieceMoved.col, move.pieceMoved.moved) # thisline self.positions[move.initialRow][move.initialCol] = "00" # 创建棋子副本,而不是直接用原对象 piece_copy = move.pieceMoved.copy() self.positions[move.endRow][move.endCol] = piece_copy # 修改副本的属性,不会影响原move里的棋子 self.positions[move.endRow][move.endCol].row = move.endRow self.positions[move.endRow][move.endCol].col = move.endCol if isinstance(piece_copy, Pawn): self.positions[move.endRow][move.endCol].moved = True self.move_log.append(move) # 记录原始move对象,方便撤销 print(move.pieceMoved.row, move.pieceMoved.col, move.pieceMoved.moved) # this line self.white_turn = not self.white_turn
这样修改后,move对象里的pieceMoved会保持原始状态,不会被棋盘里的操作修改,后续undoMove就能正常依赖原始状态了。
内容的提问来源于stack exchange,提问作者RandomUser57
相关产品推荐
相关产品推荐

