Deepcopy和迭代返回不同的结果

时间:2020-07-03 12:57:09

标签: python python-3.x

我有一个8x8数组,其中某些位置包含对象,其他位置包含' '。 (这是一个棋盘)

由于使用了copy.deepcopy(x),我的代码部分运行缓慢,因此我进行了一些测试,发现遍历数组的速度快了32倍。当我运行代码时,它引发了错误,因此我将迭代和copy.deepcopy(x)的结果进行了比较,它们并不相同。我看不到哪里出了问题,也找不到类似的东西(我看过)

该功能如下:

def makeCopy(array):
    new = [[],[],[],[],[],[],[],[]]
    for x in range(0,8):
        for y in range(0,8):
            new[x].append(array[x][y])
    a = copy.deepcopy(array)
    if new != a:
        print('failed')
    else:
        print('good')
    return new

调用它时,它会打印failed,因此它们不相同,这会破坏我的其余代码。

我正在传递类似这样的内容(某些作品的位置可能会有所不同):

currentGameState = [[Rook('black', [0,0]),Knight('black',[1,0]),Bishop('black', [2,0]),Queen('black',[3,0]),King('black',[4,0]),Bishop('black', [5,0]),Knight('black',[6,0]),Rook('black', [7,0])],
                    [Pawn('black', [0,1]),Pawn('black', [1,1]),Pawn('black', [2,1]),Pawn('black', [3,1]),Pawn('black', [4,1]),Pawn('black', [5,1]),Pawn('black', [6,1]),Pawn('black', [7,1])],
                    [' ',' ',' ',' ',' ',' ',' ',' '],
                    [' ',' ',' ',' ',' ',' ',' ',' '],
                    [' ',' ',' ',' ',' ',' ',' ',' '],
                    [' ',' ',' ',' ',' ',' ',' ',' '],
                    [Pawn('white', [0,6]),Pawn('white', [1,6]),Pawn('white', [2,6]),Pawn('white', [3,6]),Pawn('white', [4,6]),Pawn('white', [5,6]),Pawn('white', [6,6]),Pawn('white', [7,6])],
                    [Rook('white', [0,7]),Knight('white', [1,7]),Bishop('white', [2,7]),Queen('white',[3,7]),King('white',[4,7]),Bishop('white', [5,7]),Knight('white',[6,7]),Rook('white', [7,7])]]

4 个答案:

答案 0 :(得分:2)

您实际上并没有在矩阵内复制对象。

for x in range(0,8):
    for y in range(0,8):
        new[x].append(array[x][y])

使用此代码,您实际上是在两个列表列表之间的单个单元格内绑定同一对象。如果您在第一个列表的单元格中更改一个对象,则将更改另一个列表的单元格中的对象。 这就是为什么它更快

使用copy.deepcopy(array)复制单元格以及每个单元格内的对象。

Python3复制模块: https://docs.python.org/3/library/copy.html

答案 1 :(得分:1)

您的复制功能实际上可以很好地复制列表的结构,尽管可以通过使用列表理解来简化它: new = [[x for x in row] for row in array]

问题和与copy.deepcopy的不同之处在于,它没有复制像Rook('black', [0,0])这样的实际游戏作品。 从各种评论中,我得出结论:

  • 这些片段没有实现__eq__,因此==只是比较内存地址,因此您的副本被认为与deepcopy是“不同的”(但可能等于 原始文档,实际上是deepcopy与原始文档不同的
  • 作品包括其当前位置,例如[0,0],然后在移动作品时更新/修改该位置
  • 因此,将对同一棋子的引用添加到棋盘副本时,您的Minimax算法将在所有状态下“移动”棋子,从而创建许多无效的游戏状态

以下是一些可能的解决方案,它们不仅增加了所需重构的复杂性,而且还增加了(假定的)收益:

  • 添加一个copy函数,并在创建“手动”副本时调用该方法,例如我上面的清单理解中的new = [[copy(x) for x in row] for row in array];在这里,copy不是一种方法,因为这些列表中有str和游戏作品,并且copy必须为两者工作(对于str,它只能返回原始的)
  • 不是每次复制状态时都使用copy函数,而只是在移动 之前创建游戏棋子的副本,而所有其他棋子可以保持不变;这样,您只需要创建一个副本,而不是每次移动创建64个副本(不过,您仍然必须创建实际电路板的副本)
  • 完全删除Rook等类,只对{black Rook“或元组"Rb"使用("Rook", "black")这样的字符串表示形式,不要多余地包括它们的当前位置,而只是在计算可能的移动时从网格本身获取数据(这将是一个函数,而不是不再存在的类的方法)

用于前两种方法的copy函数可以例如看起来像这样简单:

def copy(piece):
    if isinstance(piece, str):
        return piece
    else:
        return type(piece)(piece.color, list(piece.position))

您还可以在循环/列表理解中的各个部分上使用copy.deepcopy,这可能比使用它复制整个面板要快一些,但是要比定制的{{1} }功能。

答案 2 :(得分:0)

当我尝试按原样运行您的代码时(使用np.ones((8,8))作为示例输入),我得到了:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

因为new != a的结果是:

[[False False False False False False False False]
 [False False False False False False False False]
 [False False False False False False False False]
 [False False False False False False False False]
 [False False False False False False False False]
 [False False False False False False False False]
 [False False False False False False False False]
 [False False False False False False False False]]

相反,使用:

def makeCopy(array):
    new = [[],[],[],[],[],[],[],[]]
    for x in range(0,8):
        for y in range(0,8):
            new[x].append(array[x][y])
    a = copy.deepcopy(array)
    if (new != a).any():
        print('failed')
    else:
        print('good')
    return new

在这里,我们使用array.any()检查列表中的元素是否不匹配。

答案 3 :(得分:0)

您的代码为我打印了“好”字,我使用整数和列表的字符串列表进行了尝试,并将正确的初始列表作为参数提供给makeCopy函数。您确定要使用列表列表来测试功能吗?