Python 继承numpy.vectorize-ed函数
在寻找一个模糊的bug时,我偶然发现了一个最能说明问题的例子:Python 继承numpy.vectorize-ed函数,python,numpy,Python,Numpy,在寻找一个模糊的bug时,我偶然发现了一个最能说明问题的例子: 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 __in
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.
First:vF=
呼叫first.vF:[1 4 9]
第一:vF=
第二:vF=
调用second.vF:
回溯(最近一次呼叫最后一次):
...
RuntimeError:永远不要在这里。
因此,似乎s.vF
和f.vF
是同一个对象,即使s.vF==f.vF
是False
这是一种预期的/已知的/文档化的行为,并且numpy.vectorize
不能很好地处理继承,还是我缺少了一些简单的东西?
(当然,在这种特殊情况下,通过将
First.vF
更改为普通的Python方法,或者只是不在第二个的构造函数中调用super
,问题很容易解决。)这与numpy无关。矢量化或numpy
通常真的
这里发生的是Second.\uuuu init\uuuuu
首先调用。\uuuu init\uuuuu
从self.F
(实际上是围绕Second.F
的实例方法包装器)创建一个实例属性(vF
),并将其作为vF
存储在实例上。现在,当您查找vF
时,您得到的是猴子补丁版本,而不是原始实例方法Second。vF
这与NumPy无关。这是完全合理的语言设计决策(以及您决定使用语言的方式)相互作用的结果:
- 实例属性优先于类属性。我相信你会同意这是合理的
- 方法是类属性,在这一点上并不特殊。我相信您会同意这是合理的(如果您不同意,请查看描述符,特别是允许
self.F
工作的绑定方法)
- 继承的实例属性附加到同一个对象,而不是附加到某个奇怪的“父代理”对象或其他对象。我相信你会同意这是合理的
总之,如果您不考虑细节,而是使用简化的心智模型(例如,心智隔离方法和“数据”属性),这些完全合理的行为可能会产生意外的行为。具体来说,在您的示例中会发生这种情况:
- 调用相应的构造函数。这要么是
First.\uuuu init\uuu
,要么是Second.\uuuu init\uuu
,它立即调用First.\uuuu init\uu
- 因此,
obj.vF
始终是首先在中创建的矢量化函数。\uuu init\uuu
用于所有obj
- 但是,每个对象的矢量化函数包装了各自对象的
self.F
。对于第二个对象,这是运行时错误
-引发second.F
您可能应该在这里使用一个常规的vF
方法,因为由于属性查找的工作方式,这允许子类轻松地重写(另请参见:MRO)