You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:20:28