Craig Gidney
Craig Gidney

Reputation: 18296

Location of documentation on special methods recognized by numpy

One of the differences between math.exp and numpy.exp is that, if you have a custom class C that has a C.exp method, numpy.exp will notice and delegate to this method whereas math.exp will not:

class C:
    def exp(self):
        return 'hey!'

import math
math.exp(C())  # raises TypeError
import numpy
numpy.exp(C())  # evaluates to 'hey!'

However, if you go to the web documentation of numpy.exp, this seems to be taken for granted. It isn't explicitly stated anywhere. Is there a place where this functionality is documented?

More generally, is there a place with a list of all such methods recognized by numpy?

Upvotes: 3

Views: 74

Answers (1)

hpaulj
hpaulj

Reputation: 231510

This isn't a special behavior of the np.exp function; it's just a consequence of how object dtype arrays are evaluated.

np.exp like many numpy functions tries to convert non-array inputs into arrays before acting.

In [227]: class C:
     ...:     def exp(self):
     ...:         return 'hey!'
     ...:     
In [228]: np.exp(C())
Out[228]: 'hey!'

In [229]: np.array(C())
Out[229]: array(<__main__.C object at 0x7feb7154fa58>, dtype=object)
In [230]: np.exp(np.array(C()))
Out[230]: 'hey!'

So C() is converted into an array, which is an object dtype *(C() isn't an iterable like [1,2,3]). Typically a numpy function, if given an object dtype array, iterates on the elements, asking each to perform the corresponding method. That explains how [228] ends up evaluating C().exp().

In [231]: np.exp([C(),C()])
Out[231]: array(['hey!', 'hey!'], dtype=object)
In [232]: np.exp([C(),C(),2])
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-232-5010b59d525d> in <module>()
----> 1 np.exp([C(),C(),2])

AttributeError: 'int' object has no attribute 'exp'

np.exp can work on an array object dtype provided all the elements have a exp method. Integers don't. ndarray doesn't either.

In [233]: np.exp([C(),C(),np.array(2)])
AttributeError: 'numpy.ndarray' object has no attribute 'exp'

math.exp expects a number, an Python scalar (or something that can convert into a scalar such as np.array(3).

I expect this behavior is common to allufunc. I don't know about other numpy functions that don't follow that protocol.

In some cases the ufunc delegates to __ methods:

In [242]: class C:
     ...:     def exp(self):
     ...:         return 'hey!'
     ...:     def __abs__(self):
     ...:         return 'HEY'
     ...:     def __add__(self, other):
     ...:         return 'heyhey'
     ...:     
In [243]: 
In [243]: np.abs(C())
Out[243]: 'HEY'
In [244]: np.add(C(),C())
Out[244]: 'heyhey'

Upvotes: 4

Related Questions