Reputation: 825
I've noticed that if I have methods with the same name of numpy methods in a class, numpy tends to use the methods defined in the class rather than its own. For example:
import numpy as np # using numpy 1.13.1
class PizzaPie:
def __init__(self, slice_angles: list, topping: str='Cheese'):
try:
if sum(slice_angles) > 360:
raise Exception('A pizza cannot have more than 360 degrees cut '
'from it.')
if sum(slice_angles) == 360:
self._angles = slice_angles
else:
self._angles = slice_angles + [360 - sum(slice_angles)]
except TypeError:
# maybe slices are described as complex numbers...?
total_pie = 0
for pizza_slice in slice_angles:
angle = (
np.arctan(pizza_slice.imag / pizza_slice.real)
* (180 / np.pi)
)
total_pie += angle
if total_pie == 360:
self._angles = slice_angles
else:
# make the left over slice a simple number in degrees.
self._angles = slice_angles + [360 - total_pie]
self._topping = topping
@property
def topping(self):
return self._topping
@topping.setter
def topping(self, new):
"""Only able to add additional toppings. Removing is too messy."""
self._topping = self._topping + ', ' + str(new)
@property
def slices(self):
"""Returns the number of slices"""
return len(self.angles)
def cos(self):
"""Returns the slice number and the cosine of the slice angle as a tuple"""
return [(sl, np.cos(a*(np.pi/180)))
for sl, a in enumerate(self._angles)
]
def imag(self):
"""Returns the imaginary portion of the slice angles. Weird huh?"""
return [(sl, np.imag(a)) for sl, a in enumerate(self._angles)]
mypie = PizzaPie([90, 45, 30, 30, 35])
my_imag_pie = PizzaPie([(20+5j), 90.0, (3+4j)], topping='Dark Matter')
print(np.cos(mypie))
print(np.imag(my_imag_pie))
(of course this code is completely fabricated and fictional, but it serves as a simple example of something I'm trying to do. And it's fun to think about Imaginary Pizzas...)
But you'll notice that running the above code will return the bound method PizzaPie.imag
instead of calling PizzaPie.imag()
(in Python 3.5.3) while PizzaPie.cos
is called when np.cos(mypie)
is written. Is this a bug in numpy 1.13? Also, what is this behavior called where numpy will prefer to use class defined methods with the same name?
Sidenote: I know that I can call np.cos(mypie)
and then np.imag(my_imag_pie))()
, but this is ugly and inconsistent. I'm also worried that if someone uses my PizzaPie class with a later version of numpy, it will break and I don't want to support different code for different versions of numpy if at all possible.
Upvotes: 1
Views: 47
Reputation: 231475
Your PizzaPie
class contains an array, but is not an ndarray
subclass. Typically a np
function tries to turn its input into an array, and then apply the function to that, possibly invoking the corresponding method.
np.cos
is compiled, but we can get an idea of what it attempts to do by giving it a list, specifically a list that can't be made into a simple numeric array:
In [51]: np.cos([np.arange(3),3,[1,2]])
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-51-0d338a087870> in <module>()
----> 1 np.cos([np.arange(3),3,[1,2]])
AttributeError: 'numpy.ndarray' object has no attribute 'cos'
In [52]: np.array([np.arange(3),3,[1,2]])
Out[52]: array([array([0, 1, 2]), 3, list([1, 2])], dtype=object)
np.cos
has turned the input into an object array and tried to do [x.cos() for x in np.array(input)]
. For your case it does work, since you defined a cos
method.
np.imag
is Python code, and attempts to return the imag
attribute (property). It assumes imag
is a property, not a method, which is why you get the unevaluated method.
np.imag(val)
return val.imag
# or
return np.asanyarray(val).imag
In [53]: np.imag([np.arange(3),3,[1,2]])
Out[53]: array([0, 0, 0], dtype=object)
Try setting imag
as a property. Then it should evaluate.
Upvotes: 2