为什么这个 numpy 属性突然在实例之间共享

Why does this numpy attribute suddenly become shared between instances

我在 Linux 下使用 python 3.6 和 numpy 1.12.1 时偶然发现了奇怪的行为。

我有一个属性 self.count,我用 np.array([0.0, 0.0, 0.0]) 对其进行了初始化。我希望 self.count 的行为与任何其他属性一样,并且每个 class 实例都有自己的值。

然而,在下面的代码中,在 addPixel 方法中,当我使用

self.count += (1.0, 1.0, 1.0)

self.count 属性在 class CumulativePixel 的所有实例中都增加了。我想了解为什么会发生这种情况以及为什么当我这样做时它会得到修复:

self.count = self.count + (1.0, 1.0, 1.0)

相反。

import numpy as np

class CumulativePixel(object):
    '''
    class adds rgb triples and counts how many have been added
    '''

    def __init__(self, rgb = (0,0,0), count=np.array([0.0, 0.0, 0.0]) ):
        '''
        Constructor
        rgb sum is stored as two values. The integer part plus float part
        they are stored in a 2x3 matrix where the first row are integer
        parts and the second row are float parts. The code always tries to 
        make sure that float part is below 1.0
        '''
        self.rgb = np.array( [np.fmod(rgb, (1,1,1)).astype(float), (rgb - np.fmod(rgb, (1,1,1)))] )
        self.count = count

    @staticmethod   
    #for now only works for positve numbers 
    def _pixeladdition (disassembled, rgb):
        disassembled += np.array( [np.fmod(rgb, (1,1,1)).astype(float), (rgb - np.fmod(rgb, (1,1,1)))] )

        fpart = np.fmod(disassembled[0], (1,1,1))
        overflowpart = disassembled[0]-fpart
        disassembled[0]=fpart
        disassembled[1]+=overflowpart

        return disassembled

    def addPixel(self, rgb):
        self.rgb = self._pixeladdition(self.rgb, rgb)     
        # += would globalize self.count into all instances! why ???
        self.count = self.count + (1.0, 1.0, 1.0)

    def getAvgPixel(self, multiply = (1.0, 1.0, 1.0), add = (0.0, 0.0, 0.0), roundpx = False):
        if 0.0 in self.count: return (0.0, 0.0, 0.0)
        averagepixel = np.sum(self._pixeladdition((self.rgb/self.count), add)*multiply, axis=0)

        if roundpx: averagepixel = np.round(averagepixel).astype(int)

        return averagepixel

    def getSums(self):
        return np.sum(self.rgb, axis=0)

    def __str__(self):
        return "count: " + str(self.count) + " integers: " + str(self.rgb[1].tolist())+ " floats: " + str(self.rgb[0].tolist())

    def __repr__(self):
        return "CumulativePixel(rgb = " + str(tuple(np.sum(self.rgb, axis=0))) + ", count=" + str(self.count) +")"

编辑: 我创建了这个 class 的实例(在另一个 class 中)如下:

self.pixeldata = [CumulativePixel() for i in range(self.imagewidth*self.imageheight)]

这是一个常见错误,在使用列表作为函数的默认值时最常见。

count=np.array([0.0, 0.0, 0.0]) 

这个数组在class初始化时创建一次。所以所有实例共享相同的 create 属性,相同的数组。他们没有得到新的数组。

当您执行 self.create +=... 时,您就地修改了它。

使用 self.create = self.create + ...,您创建了一个新数组,因此一个实例的更改不会影响其他实例。

做这样的事情是个好习惯:

 def __init__(self, create=None):
     if create is None:
        create = np.array([1,2,3,4])
     self.create = create

现在默认值将是最新的,每个实例都是唯一的。