列表推导的存储位置问题

Stored Location Issue With List Comprehensions

提问人:Coder1913 提问时间:3/14/2023 更新时间:3/14/2023 访问量:32

问:

我正在尝试用 Python 编写一个函数来创建一个具有指定维度的游戏板。这个想法是将板表示为嵌套列表,每个空格的初始值相同(并在函数的输入中指定)。为此,我编写了以下代码:

def make_board(dimensions, val, lst = []):
    if not dimensions:
        return lst
    elif len(dimensions) == 1 and not lst:
        return [val for i in range(dimensions[0])]
    elif len(dimensions) == 1 and lst:
        return [lst.copy() for i in range(dimensions[0])]
    else:
        if not lst:
            new_lst = [val for i in range(dimensions[-1])]
        else:
            new_lst = [lst.copy() for i in range(dimensions[-1])]
        return make_board(dimensions[:-1], val, new_lst)

当我最初制作电路板时,这运行良好。但是,如果我随后尝试更新其中一个空间,我会遇到它更新多个空间的问题。例如,当我运行以下命令时,我会得到以下结果:

example = make_board((2, 4, 2), 0)
print(example)
>>> [[[0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0]]]
example[1][3][0] = 4
>>> [[[0, 0], [0, 0], [0, 0], [4, 0]], [[0, 0], [0, 0], [0, 0], [4, 0]]]

出于某种我不明白的原因,似乎我在函数中的列表推导将值/列表存储在同一地址。具体来说,当我遍历长度为 2 的 8 个内部列表并打印十六进制 ID 时,对于相应的索引,十六进制 ID 是相同的(因此 example[0][0] 与 example[1][0] 具有相同的十六进制 id,依此类推)。

我注意到的一件事是,最初当我尝试将值更改为 4 时,我得到了 [[[4, 0], [4, 0], [4, 0], [4, 0]], [[4, 0], [4, 0], [4, 0], [4, 0]]]。当我在上面的函数中将 .copy() 添加到两个 lst 实例时,它对其进行了更改,以便只有一个重复项。但是,我对从这里开始去哪里感到困惑。我以前没有遇到过列表推导的问题,因此关于我还可以做些什么来尝试故障排除的任何建议将不胜感激。

当我最初制作电路板时,这运行良好。但是,如果我随后尝试更新其中一个空间,我会遇到它更新多个空间的问题。例如,当我运行以下命令时,我会得到以下结果:

example = make_board((2, 4, 2), 0)
print(example)
>>> [[[0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0]]]
example[1][3][0] = 4
>>> [[[0, 0], [0, 0], [0, 0], [4, 0]], [[0, 0], [0, 0], [0, 0], [4, 0]]]

出于某种我不明白的原因,似乎我在函数中的列表推导将值/列表存储在同一地址。具体来说,当我遍历长度为 2 的 8 个内部列表并打印十六进制 ID 时,对于相应的索引,十六进制 ID 是相同的(因此 example[0][0] 与 example[1][0] 具有相同的十六进制 id,依此类推)。

我注意到的一件事是,最初当我尝试将值更改为 4 时,我得到了 [[[4, 0], [4, 0], [4, 0], [4, 0]], [[4, 0], [4, 0], [4, 0], [4, 0]]]。当我在上面的函数中将 .copy() 添加到两个 lst 实例时,它对其进行了更改,以便只有一个重复项。但是,我对从这里开始去哪里感到困惑。我以前没有遇到过列表推导的问题,因此关于我还可以做些什么来尝试故障排除的任何建议将不胜感激。

Python 递归 列表-推导- 嵌套列表

评论


答:

1赞 harriet 3/14/2023 #1

x.copy() 返回 x 的浅拷贝(即它构造一个新列表,其中包含对原始列表中元素的引用)--相反,使用 copy.deepcopy(),递归地将原始列表中包含的元素的对象复制到新列表中,应该对你有用:

import copy

def make_board(dimensions, val, lst=[]):
    if not dimensions:
        return lst
    elif len(dimensions) == 1 and not lst:
        return [val for _ in range(dimensions[0])]
    elif len(dimensions) == 1 and lst:
        return [copy.deepcopy(lst) for _ in range(dimensions[0])]
    else:
        if not lst:
            new_lst = [val for _ in range(dimensions[-1])]
        else:
            new_lst = [copy.deepcopy(lst) for _ in range(dimensions[-1])]
    return make_board(dimensions[:-1], val, new_lst)

example = make_board((2, 4, 2), 0)
example[1][3][0] = 4
print(example)

这将返回

[[[0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [4, 0]]]