3

在寻找一个不起眼的错误时,我偶然发现了这个最小示例最能证明的东西:

import numpy as np

class First(object):
    def __init__(self):
        self.vF = np.vectorize(self.F)
        print "First: vF = ", self.vF

    def F(self, x):
        return x**2


class Second(First):
    def __init__(self):
        super(Second, self).__init__()
        print "Second: vF = ", self.vF

    def F(self, x):
        raise RuntimeError("Never be here.")

    def vF(self, x):
        return np.asarray(x)*2

我希望 的实例Second有一个明确定义的vF方法,但情况似乎并非如此:

arg = (1, 2, 3)

f = First()       
print "calling first.vF: ", f.vF(arg)

s = Second()
print "calling second.vF: ", s.vF(arg)

生产

First: vF =  <numpy.lib.function_base.vectorize object at 0x23f9310>
calling first.vF:  [1 4 9]
First: vF =  <numpy.lib.function_base.vectorize object at 0x23f93d0>
Second: vF =  <numpy.lib.function_base.vectorize object at 0x23f93d0>
calling second.vF: 
Traceback (most recent call last):
...
RuntimeError: Never be here.

所以看起来s.vFf.vF是同一个对象,即使s.vF == f.vFFalse

这是预期/已知/记​​录的行为,并且numpy.vectorize不能很好地继承,还是我在这里遗漏了一些简单的东西?(当然,在这种特殊情况下,问题很容易通过更改First.vF为普通的 Python 方法来解决,或者只是不调用superSecond构造函数。)

4

2 回答 2

2

这与 NumPy 无关。这是完全合理的语言设计决策(以及您决定使用该语言的方式)相互作用的结果:

  • 实例属性优先于类属性。我相信你会同意这是合理的。
  • 方法是类属性,并没有什么特别之处。我相信您会同意这是合理的(如果您不同意,请查看描述符,特别是允许self.F工作的绑定方法)。
  • 继承的实例属性附加到同一个对象,而不是一些奇怪的“父代理”对象或其他东西。我相信你会同意这是合理的。

结合起来,如果您不牢记细节而是使用简化的心理模型(例如心理分离方法和“数据”属性),这些完全合理的行为可能会产生意想不到的行为。详细地说,这发生在您的示例中:

  • 调用相应的构造函数。这是要么First.__init__,要么Second.__init__立即调用First.__init__
  • 因此,obj.vF始终First.__init__为 all创建的矢量化函数obj
  • 但是,每个对象的矢量化函数都包装self.F了相应对象的 。在第二个对象的情况下,这是RuntimeError-raise Second.F

您可能应该在这里只使用常规vF方法,因为由于属性查找的工作方式,这允许子类轻松覆盖(另请参见:MRO)。

于 2013-02-13T18:00:43.347 回答
1

numpy.vectorize这与或numpy一般来说真的无关......

这里发生的是创建实例属性 ( ) 的Second.__init__调用(实际上是一个实例方法包装器)并将其存储在实例上。现在,当您查找时,您会得到猴子补丁版本而不是原始实例方法。First.__init__vFself.FSecond.FvFvFSecond.vF

于 2013-02-13T17:58:12.783 回答