Reputation: 18296
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
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