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

).

+3


source to share


2 answers


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__

    or Second.__init__

    , which immediately calls First.__init__

    .
  • Hence, obj.vF

    it is always a vectorized function created in First.__init__

    for everyone obj

    .
  • However, each object vector function wraps the self.F

    corresponding object. In the case of the second object, it is RuntimeError

    -raising Second.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).

+2


source


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

.

+1


source







All Articles