类属性的奇怪行为,__ iadd __(+ =)和numpy.random.randn()

时间:2017-11-07 22:19:59

标签: python numpy random

我一直使用Python和Numpy为随机过程建模,并使用以下代码见证奇怪的行为:

import numpy as np

class Example( object ):
    def __init__( self ):
        self.x = 0

    def add_random( self ):
        self.x += np.random.randn(1)
        return self.x

if __name__ == '__main__':
    example = Example()
    state = []
    for x in range(10):
        state.append( example.add_random() )
    print state

这将返回10个相同随机数的数组,而不是预期的10个不同的随机数。取消object.__iadd__运算符和/或用常量替换np.random.randn(.)将解决问题。任何人都知道这是什么根源?

2 个答案:

答案 0 :(得分:4)

np.random.randn(1)返回一个包含单个元素的数组:

In [27]: np.random.randn(1)
Out[27]: array([-1.90409169])

第一次执行此行

self.x += np.random.randn(1)

self.x - 最初是一个Python整数 - 被一个numpy数组取代。该行的后续执行会修改x就地,因为这是numpy数组如何实现就地添加。因此return self.x始终返回相同的数组。因此,您在主要部分中创建的列表是包含重复10次的同一对象的列表。

解决此问题的一种方法是使用np.random.randn()代替np.random.randn(1)np.random.randn()返回一个标量,因此赋值self.x += np.random.randn(1)每次执行时都会创建一个新的self.x对象。

答案 1 :(得分:2)

您将通过引用返回数组self.x,因此您有10个指向同一数组的指针。每次修改数组时,所有十个指针都指向相同的修改版本。

如果您希望每次都返回数组的单独副本,则return self.x.copy()函数中可以add_random

另一种解决方法是将np.random.rand(1)替换为np.random.rand(),以便self.x保持标量而不是向上转换为数组。