Python 3.x 无法附加到数组元素

Python 3.x 无法附加到数组元素,python-3.x,numpy,Python 3.x,Numpy,我尝试使用以下函数将字符串附加到数组元素的末尾: def spread(farm): #writing potential fruit spread for y in range(0,len(farm[0])): for x in range(0,len(farm[0])): #if this current space is a tree, write the potential spaces accordingly

我尝试使用以下函数将字符串附加到数组元素的末尾:

def spread(farm):
    #writing potential fruit spread
    for y in range(0,len(farm[0])):
        for x in range(0,len(farm[0])):
            #if this current space is a tree, write the potential spaces accordingly
            if farm[y][x] == "F" or farm[y][x] == "W" or farm[y][x] == "G" or farm[y][x] == "J" or farm[y][x] == "M":
                for b in [-1,0,1]:
                    #making sure the y-coord is within the bounds of the farm
                    if y+b >= 0 and y+b < len(farm[0]):
                        for a in [-1,0,1]:
                            #making sure the x-coord is within the bounds of the farm and the selected space is not a tree
                            if x+a >= 0 and x+a < len(farm[0]) and farm[y+b][x+a] != "F" and farm[y+b][x+a] != "W" and farm[y+b][x+a] != "G" and farm[y+b][x+a] != "J" and farm[y+b][x+a] != "M":
                                #if space is blank, write over the space outright
                                if farm[y+b][x+a] == "_":
                                    farm[y+b][x+a] = farm[y][x].lower()
                                else:
                                    #wherein my troubles lie :(
                                    farm[y+b][x+a] = farm[y+b][x+a] + farm[y][x].lower()
    return farm
该函数的作用是模拟果树的生长。每棵树(用大写字母表示)将扩展到相邻的方块(用小写字符或下划线表示)。但是,最后一行处理所选数组元素不是下划线的情况。应该发生的是,它会将字符串追加到数组元素的末尾,而不是替换它,但不会追加任何内容。输出应该如下所示:

[['_' '_' 'fw' 'F' 'fw' '_' '_']
 ['_' '_' 'fw' 'W' 'fw' '_' '_']
 ['_' '_' 'wj' 'wj' 'wj' '_' '_']
 ['_' '_' 'j' 'J' 'j' '_' '_']
 ['g' 'g' 'jg' 'j' 'jf' 'f' 'f']
 ['gw' 'G' 'g' '_' 'f' 'F' 'fg']
 ['W' 'gw' 'g' '_' 'f' 'fg' 'G']]
但它输出的却是:

[['_' '_' 'f' 'F' 'f' '_' '_']
 ['_' '_' 'f' 'W' 'f' '_' '_']
 ['_' '_' 'w' 'w' 'w' '_' '_']
 ['_' '_' 'j' 'J' 'j' '_' '_']
 ['g' 'g' 'j' 'j' 'j' 'f' 'f']
 ['g' 'G' 'g' '_' 'f' 'F' 'f']
 ['W' 'g' 'g' '_' 'f' 'f' 'G']]

我做错了什么?

带有字符串数据类型的NumPy数组将自动截断您试图存储的、对于数据类型来说太大的任何字符串。使用列表列表。

如上所述,Numpy有自己的字符串类型,它限制了所包含文本的长度,因此数据可以以整洁的“矩形”方式存储,而无需间接寻址。(例如,这意味着Numpy可以简单地进行数学运算来计算任何元素的位置,而不是追逐多个索引的指针。)

创建数组时,可以通过显式指定
dtype=object
来解决这个问题。这意味着Numpy将在其内部表示中存储指向Python对象的指针;这失去了很多好处,但仍然可以让您根据任务编写总体上更快、更优雅的代码

让我们试着在这里实现它。我的第一个建议是使用空的
'
字符串,而不是
'
;这从我们的逻辑中删除了一堆特殊情况(我们都知道,“特殊情况不足以打破规则”)

因此,我们从以下方面开始:

farm = np.array([
    ['', '', '', 'F', '', '', ''],
    ['', '', '', 'W', '', '', ''],
    ['', '', '', '', '', '', ''],
    ['', '', '', 'J', '', '', ''],
    ['', '', '', '', '', '', ''],
    ['', 'G', '', '', '', 'F', ''],
    ['W', '', '', '', '', '', 'G']
], dtype='object')
Numpy在这方面帮助我们的主要方式是它可以有效地:

  • 将运算符和函数应用于数组的每个元素elementwise
  • 在一个或多个维度中切片数组
我的做法如下:

  • 创建一个函数,告诉我们从本地树上种植了哪些树苗

  • 使用Numpy创建一个数组,其中包含从农场中相应树木种植的所有树苗

  • 创建一个函数,通过对树苗数组进行切片并将新树苗“添加”(
    +
    ,当然是字符串连接)到农场的相应切片,在与源树偏移的位置种植树苗

  • 重复树苗可以种植的方向,完成所有的种植

  • 那么,让我们来看一下

    第一步非常简单:

    # Determine the saplings that will be planted, if any, from a given source plot.
    # Handling the case where multiple trees are already present, is left as an exercise.
    def sapling_for(plot):
        return plot.lower() if plot in 'FGJMW' else ''
    
    现在我们需要将其应用于整个阵列。像
    +
    这样的运算符的应用是自动的。(如果您有两个数组
    x
    y
    ,它们的维数和大小都相同,您可以将它们与
    x+y
    相加,所有内容都按元素相加。请注意,
    x*y
    不是矩阵乘法,而是按元素乘法。),对于用户定义的函数,我们需要做一些额外的工作-我们不能将
    农场
    传递给
    树苗(毕竟,它没有
    .lower()
    方法,这只是许多问题中的一个)。它看起来像:

    saplings = np.vectorize(sapling_for)(farm)
    
    好的,不太难。继续进行切片。这有点棘手。例如,我们可以很容易地获得
    树苗的西北部分
    :它是
    树苗[:-1,:-1]
    (即除了最后一行和最后一列之外的所有内容)。注意,我们没有做两个单独的索引操作——这是Deep NumPy Magic(TM),我们需要按照NumPy的方式来做

    我在这里的想法是,我们可以表示树苗“蔓延”到东南部,方法是将这一西北部分添加到农场的东南部分:
    农场[1:,1::+=树苗[:-1,:-1]
    。我们可以简单地为每个罗盘的传播方向做八次。但是概括一下呢

    这有点棘手,因为例如
    1:
    本身并不意味着什么。但是,它确实有一个内置的Python表示:原生的
    切片
    类型,我们也可以将其用于Numpy索引(以及为内置序列类型编制索引!)。因此,我编写了一个helper函数来创建:

    def get_slice(dx):
        return slice(dx, None, None) if dx >= 0 else slice(None, dx, None)
    
    这些类似于
    范围
    对象:参数为起点、终点和“步长”。这里的想法是,负值将使一个切片从末端去掉那么多项目,而正值将使它们从前端去掉

    这使我们可以编写一个通用函数,将一个数组的一个片段添加到“基本”数组的移位位置(在相反的角点):

    希望逻辑足够清晰。最后,我们可以迭代所有(dx,dy)对来传播树苗:一个空间内的任何地方,除了
    (0,0)


    我们完成了。

    如果列表的一个成员应该有不同的值
    lst[0][2]=“fw”
    该“farm”看起来可疑地像一个NumPy数组,则可以通过索引进行修改。您正在为此使用NumPy数组吗?如果是,为什么?NumPy数组的固定内存布局会导致这样的问题。这是GoL的演变吗?如果将数组初始化为类似于
    U10
    ,则不会得到截断。但是,由于您正在迭代并执行逐元素操作,
    numpy
    与列表相比没有任何优势。对于这种工作,列表更快。有趣。它究竟为什么会这样做?因为Numpy的全部意义在于表示固定大小的矩形存储。这就是它能做的所有优化的原因。同样的原因是,存储整数的Numpy数组将为整数指定字节大小,而不是使用本机任意大小的Python
    def get_slice(dx):
        return slice(dx, None, None) if dx >= 0 else slice(None, dx, None)
    
    def add_shifted(base, to_add, dx, dy):
        base[get_slice(dx), get_slice(dy)] += to_add[get_slice(-dx), get_slice(-dy)]
    
    for dx in (-1, 0, 1):
        for dy in (-1, 0, 1):
            if dx != 0 or dy != 0:
                add_shifted(farm, saplings, dx, dy)