0

我正在尝试编写可以以二维数组的形式构建某种可编辑世界地图的代码:

class map(object):
    def __init__(self, width, height):
        self.__height = height
        self.__width = width

        self.__grid = []
        row = []
        for num in range(0, self.__width):
            row.append(".")
        for num in range(0, self.__height):
            self.__grid.append(row)
        print(self.__grid)

    def drawCell(self, X, Y, symbol):
        self.__grid[Y-1][X-1] = symbol
        print(self.__grid)

world = map(6, 4)
world.drawCell(3, 2, "0")

当我运行它时,它似乎不是编辑单元格 (3, 2),而是在每一行中执行第三个单元格。奇怪的是,当我将init () 更改为此...

    def __init__(self, width, height):
        self.__height = height
        self.__width = width

        self.__grid = [['.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.']]

...因此init跳过了网格的构建,它完美地工作!看起来它正在附加变量“row”而不是它包含的值,因此它编辑了“row”的每个实例。

当然,上面的这个解决方案不允许改变地图大小。所以我的问题是:有没有一种非冗余的方法来解决这个问题?

编辑:你不需要解释为什么会这样,我已经解释过了。我想象当我做这个时它附加了值,而不是包含它的变量。

4

8 回答 8

1

您的网格生成是错误的。

row = []
for num in range(0, self.__width):
    row.append(".")
for num in range(0, self.__height):
    self.__grid.append(row)

您每次都附加相同的(从某种意义上说,它实际上是相同的,同一个列表的多个引用)行(请注意,row它只定义了一次!)。将其更改为:

for num in range(0, self.__height):
    row = []
    for num2 in range(0, self.__width):
        row.append(".")
    self.__grid.append(row)

如您所见,这不是 Python 中的错误。:)

于 2013-08-13T14:53:44.953 回答
1

您正在创建一个列表,然后将同一列表附加到self.__grid self.__height时间。

改为附加副本:

for num in range(0, self.__height):
    self.__grid.append(row[:])

或者更好的是,使用生成器表达式生成整个网格:

self.__grid = [['.'] * self.__width for _ in self.__height]
于 2013-08-13T14:54:04.083 回答
0

这是因为当你这样做时:

self.__grid.append(row)

在第一个示例中,您将相同的row对象附加到每一行。如果你想避免这种情况,你可以这样做:

self.__grid.append(row[:])

创建副本。

于 2013-08-13T14:55:24.953 回答
0

简单的 NumPy 实现:D

import numpy as np  

world = np.ones(shape = (6,4)).astype(str) #YourGrid
world[3,2] = "0" #DrawCell

print world

产量:

[['1.0' '1.0' '1.0' '1.0']
 ['1.0' '1.0' '1.0' '1.0']
 ['1.0' '1.0' '1.0' '1.0']
 ['1.0' '1.0' '0' '1.0']
 ['1.0' '1.0' '1.0' '1.0']
 ['1.0' '1.0' '1.0' '1.0']]
于 2013-08-13T14:56:57.800 回答
0

那是因为当你做self.__grid.append(row)了很多次时,它每次都会附加一个对同一个列表对象的引用,所以当你编辑对其中一个的引用时,你正在编辑所有引用后面的对象。

这将做你想要的:

    self.__grid = []
    for num in range(0, self.__height):
        row = []
        for num in range(0, self.__width):
            row.append(".")
        self.__grid.append(row)

但如果你使用列表推导,它会更紧凑:

self.__grid = [['.' for a in range(self.__width)] for b in range(self.__height)]

这使得每一行都成为对单独列表对象的引用。

于 2013-08-13T14:53:07.977 回答
0

您的问题是您没有构建(高度)不同的行,而是构建了一个行列表并将其附加到self.__grid (高度)次。这意味着更改一行将更改所有其他行,因为self.__grid引用相同列表对象的所有元素。

在您的原始代码中,您可以通过更改此行来解决此问题:

self.__grid.append(row)

要改为制作模板行的副本:

self.__grid.append(list(row))
于 2013-08-13T14:53:36.687 回答
0

本质上,python 中的列表是一个对象,当您将列表/对象分配给变量时,您只是将指针传递给它。当您将列表附加到另一个列表时也是如此。因此,在这种情况下,您最终会多次将指针附加到同一个对象(这就是为什么当您编辑一个对象时您会全部编辑)。

您必须重新创建多个实例(查找列表理解,一种初始化多维列表的简洁方法),或者在附加列表时复制列表。

于 2013-08-13T16:37:52.010 回答
0

很好的答案,但我还要提到它可以缩短为

self.__grid = [['.'] * self.__height for i in range(self.__width)]
于 2013-08-13T16:07:47.293 回答