PEP 3141 introduces abstract base classes for different kinds of numbers to allow custom implementations.
I want to derive a class from numbers.Real
and calculate its sine value. Using pythons math
-module, this works fine. When I try the same in numpy, I get an error.
from numbers import Real
import numpy as np
import math
class Mynum(Real):
def __float__(self):
return 0.0
# Many other definitions
a = Mynum()
print("math:")
print(math.sin(a))
print("numpy:")
print(np.sin(a))
results in
math: 0.0 numpy: AttributeError: 'Mynum' object has no attribute 'sin' The above exception was the direct cause of the following exception: Traceback (most recent call last): [...] in <module> print(np.sin(a)) TypeError: loop of ufunc does not support argument 0 of type Mynum which has no callable sin method
It seems like numpy tries to call a sin
-method of its argument. To me, this is quite confusing since the standard data types (like float
) do not have such a method either, but np.sin
works on them.
Is there just some kind of hardcoded check for standard data types, and PEP 3141 is not supported? Or did I miss something in my class?
Because it is quite tedious to implement all required methods, here is my current code that works with the math
-module:
from numbers import Real
import numpy as np
import math
class Mynum(Real):
def __init__(self):
pass
def __abs__(self):
pass
def __add__(self):
pass
def __ceil__(self):
pass
def __eq__(self):
pass
def __float__(self):
return 0.0
def __floor__(self):
pass
def __floordiv__(self):
pass
def __le__(self):
pass
def __lt__(self):
pass
def __mod__(self):
pass
def __mul__(self):
pass
def __neg__(self):
pass
def __pos__(self):
pass
def __pow__(self):
pass
def __radd__(self):
pass
def __rfloordiv__(self):
pass
def __rmod__(self):
pass
def __rmul__(self):
pass
def __round__(self):
pass
def __rpow__(self):
pass
def __rtruediv__(self):
pass
def __truediv__(self):
pass
def __trunc__(self):
pass
a = Mynum()
print("math:")
print(math.sin(a))
print("numpy:")
print(np.sin(a))
I just answered something like this, but I'll repeat myself
np.sin(a)
is actually
np.sin(np.array(a))
What does np.array(a)
produce? What's its dtype
?
If it's an object dtype array, that explains that error. With object dtype array, numpy
iterates through (the references), and tries to run an appropriate method on each. That's generally ok with operators which can use __add__
like methods, but almost no one defines a sin
or exp
method.
From yesterday
How can I make my class more robust to operator/function overloading?
Comparing a numeric dtype array with an object dtype:
In [428]: np.sin(np.array([1,2,3]))
Out[428]: array([0.84147098, 0.90929743, 0.14112001])
In [429]: np.sin(np.array([1,2,3], object))
AttributeError: 'int' object has no attribute 'sin'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<ipython-input-429-d6927b9a87c7>", line 1, in <module>
np.sin(np.array([1,2,3], object))
TypeError: loop of ufunc does not support argument 0 of type int which has no callable sin method