Reputation: 13218
If I do
a = [1, 2, 7]
a - a[-1]
I get TypeError: unsupported operand type(s) for -: 'list' and 'int'
However, I have a list b
made of np.float64
, and the following code works:
type(b)
# list
b - b[-1]
# array([ 281.04209146, 6.57013103, 0. ])
I think this is because the numbers in b
are np.float64
, and b
is somewhat cast to np.array
, then broadcasting happens.
But I still found this behavior surprising: since all elements in a list do not need to have the same type, what if b[0]
had been a string ? The operands in b - b[-1]
would still have been list
and np.float64
, so why is b - b[-1]
not raising a TypeError
?
EDIT: Someone answered saying that list
and np.array
are different; well, I know. But there b
is not a np.array
. It is behaving like one, but its type is list
, like I've stated in the code snippet.
Here's a minimal working example for b
:
b
# [1598.717274996219, 1324.245314569733, 1317.6751835362861]
type(b[0])
# numpy.float64
Upvotes: 2
Views: 251
Reputation: 231335
In an expression like A-B
, the interpreter can either implement it as A.__sub__(B)
or B.__rsub__(A)
.
Lists implement mul
and add
but not sub
In [29]: [1,2,3]*3
Out[29]: [1, 2, 3, 1, 2, 3, 1, 2, 3]
In [30]: [1,2,3]+[1]
Out[30]: [1, 2, 3, 1]
In [31]: [1,2,3]-3
TypeError: unsupported operand type(s) for -: 'list' and 'int'
np.ndarray
implements a __rsub__
In [32]: [1,2,3]-np.array([1,2,3])
Out[32]: array([0, 0, 0])
# np.array([1,2,3]).__rsub__([1,2,3])
And that method tries to turn the LHS into an array, so this expression is the same as:
In [33]: np.asarray([1,2,3]) - np.array([1,2,3])
Out[33]: array([0, 0, 0])
If the list contains strings, this fails:
In [35]: ['a',2,3]-np.array([1,2,3])
TypeError: ufunc 'subtract' did not contain a loop with signature matching types dtype('<U11') dtype('<U11') dtype('<U11')
because that list becomes an array of strings:
In [36]: np.asarray(['a',2,3])
Out[36]:
array(['a', '2', '3'],
dtype='<U1')
and array subtraction with string dtype is not implemented.
All that I wrote with the array RHS applies if it is np.float64
. np.float64(10)
behaves (in most contexts) the same as np.array(10.0)
.
So all these subtractions with b
are the same:
b = [np.float64(10), np.float64(1)]
b - b[-1]
b - np.float64(1)
b - np.array(1.0)
np.array(b) - np.array(1.0)
In sum, if the RHS is some sort of array, it turns the LHS list into an array as well. From there it's a question of whether the 2 arrays are compatible (in shape and dtype).
Upvotes: 2
Reputation: 14399
You don't get the same TypeError
when you make b[0]
a string though.
b=[np.float_(a_) for a_ in a]
b
Out[4]: [1.0, 2.0, 7.0]
b-b[-1]
Out[5]: array([-6., -5., 0.])
b[0]='a'
b
Out[7]: ['a', 2.0, 7.0]
b-b[-1]
Traceback (most recent call last):
File "<#>", line 1, in <module>
b-b[-1]
TypeError: ufunc 'subtract' did not contain a loop with signature matching types dtype('<U32') dtype('<U32') dtype('<U32')
You get a TypeError
for mismatching types in subtract
. In the original, when you get a np.float64
for b[-1]
, it casts the whole expression to numpy arrays. So if b[-1]
is a string:
b[2]='a'
b-b[-1]
Traceback (most recent call last):
File "<#>", line 1, in <module>
b-b[-1]
TypeError: unsupported operand type(s) for -: 'list' and 'str'
you're back to the original TypeError
.
Upvotes: 1