Deepcopy和迭代返回不同的结果

2024-06-16 11:38:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个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])]]

Tags: 对象代码innewfor数组bishoparray
3条回答

实际上,您并没有复制矩阵中的对象

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

使用这段代码,实际上是在两个列表之间的单个单元格中绑定同一个对象。执行此操作时,如果更改第一个列表单元格中的一个对象,它将更改另一个列表单元格中的对象。 这就是为什么它更快的原因

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

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

当我尝试按原样运行代码时(使用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()检查列表(列表)中的任何元素是否不匹配

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

问题是copy.deepcopycopy.deepcopy的区别在于,它没有复制像Rook('black', [0,0])那样的实际游戏片段。 从各种评论中,我得出以下结论:

  • 这些片段没有实现^{,因此==只是比较内存地址,因此您的副本被认为与deepcopy“不同”(但可能等于原件,事实上,deepcopy与原件不同
  • 工件包括其当前位置,例如[0,0],移动工件时更新/修改此位置
  • 因此,当向棋盘副本添加对同一棋子的引用时,你的极小极大算法将“移动”所有状态下的棋子,从而创建大量无效的游戏状态

以下是一些可能的解决方案,它们增加了所需重构的复杂性,但也增加了(假定的)好处:

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

前两种方法中使用的copy函数可能看起来很简单:

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

您还可以在循环/列表理解中的各个部分上使用copy.deepcopy,这可能比使用它复制整个电路板要快一些,但要比定制的copy函数慢得多

相关问题 更多 >