Hi guys im just a rookie in python (even in programming) so my question might sound very basic but i have a hard time to understand this.
Why is the selective behavi
@Warren points out that square
'delegates' to multiply. I verified this by making an object array that includes a list:
In [524]: arr = np.array([np.arange(3), 3, [3,4]])
In [525]: np.square(arr)
TypeError: can't multiply sequence by non-int of type 'list'
square
works on the rest of the array:
In [526]: np.square(arr[:2])
Out[526]: array([array([0, 1, 4]), 9], dtype=object)
sqrt
doesn't work on any of these:
In [527]: np.sqrt(arr)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-527-b58949107b3d> in <module>()
----> 1 np.sqrt(arr)
AttributeError: 'numpy.ndarray' object has no attribute 'sqrt'
I can make sqrt
work with a custom class:
class Foo(float):
def sqrt(self):
return self**0.5
In [539]: arr = np.array([Foo(3), Foo(2)], object)
In [540]: np.square(arr)
Out[540]: array([9.0, 4.0], dtype=object)
In [541]: np.sqrt(arr)
Out[541]: array([1.7320508075688772, 1.4142135623730951], dtype=object)
Not sure if this is the reason but my guess is:
What if your objects were instances of your own homemade class? Numpy cannot do anything else but hope that you have defined a sqrt
method in your class and let it do the work. So I assume that's what it does here, but in your case your objects are floats, and although np.sqrt(some_random_float)
makes sense, some_random_float.sqrt()
doesn't because the AttributeError: 'float' object has no attribute 'sqrt'
.
Typically numpy is designed and optimized for numerical computations, while using it with generic dtype=object
can be sometime convenient, you shouldn't assume everything will always run seamlessly in this case...
In Python, everything is an object.
Operators are implemented on objects either as special methods (as seen from the __
prefix in the method name, such as a + b
is syntactic sugar for a.__add__(b)
or they are aliases for builtin functions that take the objects as arguments (such as a ** b
is syntatic sugar for pow(a, b)
. And often, the functions themselves are aliases back to special methods on the objects (like iter(a)
just returns a.__iter__()
).
Numpy adds further syntactic sugar, where it implements functions that behave based on the type of numpy object. As stated above, a numpy array with dtype of object pushes the implementation of the operator back down to the object type of the element in the array (so basically, np.square(a)
is semantically similar to array = map(lambda x: x*x, array)
which is really evaluated as array = map(lambda x: x.__mult__(x), array)
.
Note that sqrt
does not exist as a builtin function (it is either imported from the math
module in the standard library or np's implementation or by using **0.5
(which is really pow(x, 0.5)
), and therefore a normal float
object will not have a method that implements it.