Numpy.vectorize-ed function inheritance
While looking for an incomprehensible error, I stumbled upon something that is best demonstrated with this minimal example:
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
I would have expected the instance Second
to have an explicitly defined method vF
, but that doesn't seem to be the case:
arg = (1, 2, 3)
f = First()
print "calling first.vF: ", f.vF(arg)
s = Second()
print "calling second.vF: ", s.vF(arg)
produces
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.
so it appears that s.vF
and f.vF
is the same object, though s.vF == f.vF
- False
.
Is this expected / known / documented behavior and numpy.vectorize
not playing well with inheritance, or am I missing something simple here? (of course, in this particular case, the problem is easy to fix, either by changing First.vF
to a regular Python method, or simply not calling super
in the constructor Second
).
source to share
This has nothing to do with NumPy. This is a consequence of the interplay of perfectly reasonable language design decisions (and how you choose to use the language):
- Instance attributes take precedence over class attributes. I'm sure you will agree that this is reasonable.
- Methods are class attributes, not special ones. I'm sure you will agree that this makes sense (if you don't, take a look at the descriptors, in particular the related methods that make it
self.F
work). - Inherited instance attributes are bound to the same object and not to some strange "parent proxy" object or whatever. I'm sure you will agree that this is reasonable.
When combined, these perfectly reasonable behaviors can lead to unexpected behavior if you ignore the details and instead work with a simplified mental model (such as mentally separating methods and data attributes). This happens in detail in your example:
- The appropriate constructor is called. It is either
First.__init__
orSecond.__init__
, which immediately callsFirst.__init__
. - Hence,
obj.vF
it is always a vectorized function created inFirst.__init__
for everyoneobj
. - However, each object vector function wraps the
self.F
corresponding object. In the case of the second object, it isRuntimeError
-raisingSecond.F
.
You should probably just use the normal method vF
as it makes it easy to override subclasses because of the way attribute lookup works (see also: MRO).
source to share
It has nothing to do with numpy.vectorize
or numpy
at all ...
This is where the following happens: Second.__init__
calls First.__init__
, which creates an instance attribute ( vF
) from self.F
(which actually wraps an instance method around Second.F
) and stores that as vF
on the instance. Now when you look vF
you get the monkey patch version, not the original instance method Second.vF
.
source to share