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

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 方法,或者只是不在 super 的构造函数中调用 Second ,问题很容易解决。)

最佳答案

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

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

  • 结合起来,这些完全合理的行为可能会产生意想不到的行为,如果您不牢记细节而是使用简化的心智模型(例如,心智分离方法和“数据”属性)。详细地说,这发生在您的示例中:
  • 调用各自的构造函数。这要么是 First.__init__ ,要么是立即调用 Second.__init__First.__init__
  • 因此, obj.vF 始终是在 First.__init__ 中为所有 obj 创建的向量化函数。
  • 然而,每个对象的矢量化函数都包装了相应对象的 self.F。在第二个对象的情况下,这是 RuntimeError 提升 Second.F

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

    关于python - 继承 numpy.vectorize 函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14859840/

    10-12 16:03