2017-12-02 12 views
0

Linuxでは、python 3.6とnumpy 1.12.1を使用すると奇妙な動作に遭遇しました。このnumpyの属性がインスタンス間で突然共有されるのはなぜですか?

私はnp.array([0.0, 0.0, 0.0])で初期化する属性self.countを持っています。 self.countは他の属性と同様に動作し、クラスインスタンスごとに独自の値を持つことが期待されます。

しかし、addPixel方法では以下のコードで私は

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

self.count属性はクラス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) +")" 

編集: 次のように私は(まだ別のクラスで)このクラスのインスタンスを作成します。デフォルトとしてリストを使用している場合

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

答えて

1

これは一般的なバグは、最も頻繁に見られています関数の値。

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

この配列は、クラスの初期化時に1回作成されます。したがって、すべてのインスタンスは同じ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 

今デフォルト値はインスタンスごとに一意の、新鮮になります。

+0

これは、内部デフォルト値が実際には特定の値であることが多いにもかかわらず、多くのライブラリがデフォルトとしてNoneを使用する理由と考えられます。また、同じポインタの複数のコピーで終わるリストの乗算([object] * number)を思い出させます。 – evolution

関連する問題