【问题标题】:Python 3.3 Bug? Appending a list to a list:Python 3.3 错误?将列表附加到列表:
【发布时间】:2013-08-15 06:15:32
【问题描述】:

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

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”的每个实例。

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

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

【问题讨论】:

标签: python arrays list append


【解决方案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 中的错误。 :)

【讨论】:

  • 谢谢!我觉得它应该看起来像一个嵌套的 for 循环,但我认为这并不重要。
【解决方案2】:

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

改为追加副本:

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

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

self.__grid = [['.'] * self.__width for _ in self.__height]

【讨论】:

  • 谢谢!我认为freakish的解决方案更简洁一些,但这教会了我一个有用的python技巧来解决这个问题。
【解决方案3】:

那是因为当您多次执行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)]

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

【讨论】:

    【解决方案4】:

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

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

    self.__grid.append(row)
    

    改为复制模板行:

    self.__grid.append(list(row))
    

    【讨论】:

      【解决方案5】:

      这是因为当你这样做时:

      self.__grid.append(row)
      

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

      self.__grid.append(row[:])
      

      创建一个副本。

      【讨论】:

        【解决方案6】:

        简单的 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']]
        

        【讨论】:

          【解决方案7】:

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

          self.__grid = [['.'] * self.__height for i in range(self.__width)]
          

          【讨论】:

            【解决方案8】:

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

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

            【讨论】:

              猜你喜欢
              • 2021-11-13
              • 1970-01-01
              • 2014-07-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-08-19
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多