Reputation: 10375
This works:
x = ['foo', 'bar']
y = [*x]
print(y) # prints ['foo', 'bar']
but this doesn't:
x = ['foo', 'bar']
y[*x] # raises SyntaxError (not NameError!)
How can I unpack tuples while indexing?
Here are two examples where I'd like to use this approach, but I'm more interested in understanding why *
-unpacking seems not to be supported in indexing in general.
import numpy as np
def lookup(a: np.ndarray, coordinates: tuple) -> float:
return a[*coordinates]
a1 = np.zeros((2, 2))
print(lookup(a1, (0, 1)) # Should print 0
a2 = np.zeros(2, 2, 2))
print(lookup(a2, (0, 0, 1)) # Should print 0
or
from typing import Tuple
NUM_DIMENSIONS = 2 # Might change at a later point in time
# Should be equivalent to Tuple[float ,float]
Result = Tuple[*([float] * NUM_DIMENSIONS)]
def get() -> Result:
...
Upvotes: 2
Views: 1374
Reputation: 589
x = ['foo', 'bar']
y[*x] # raises SyntaxError (not NameError!)
This is raising syntax error because the * and x are being taken as two separate things, rather than one. For eg : if I do the following
type(*x)
this returns the error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type() takes 1 or 3 arguments
which means that *x is not treated as a single entity but two separate.
Also,
>>> x = ['foo', 'bar']
>>> print(*x)
foo bar
>>> print(x)
['foo', 'bar']
>>> y[*x]
File "<stdin>", line 1
y[*x]
^
SyntaxError: invalid syntax
>>> y[foo bar] #this is y[*x]
File "<stdin>", line 1
y[foo bar]
^
SyntaxError: invalid syntax
Upvotes: -1
Reputation: 10375
Python's indexing already has built-in support for tuples in general (not just for NumPy), so there is no need for unpacking here.
In general, foo[x]
is syntactic sugar for type(foo).__getitem__(foo, x)
. Let's see how that works in detail:
class Foo:
def __getitem__(self, key):
print(repr(key))
foo = Foo()
If we index into foo
with a single value then it is passed to __getitem__
unchanged, no matter whether it is a scalar, a list, or a tuple:
foo[0] # prints 0
foo[(0, 1)] # prints (0, 1)
foo[[0, 1]] # prints [0, 1]
The interesting case is what happens when we supply multiple values directly while indexing (without wrapping them in a tuple or list):
foo[0, 1] # prints (0, 1)
So multiple values are automatically wrapped in a tuple! foo
cannot distinguish between foo[0, 1]
and foo[(0, 1)]
. This is because in the Python grammar, the index is an expression (or a slice, but that doesn't apply here) -- and in an expression, a ,
forms a tuple:
x = 1, 2
print(repr(x)) # prints (1, 2)
Hence, argument parsing in indexing works differently then for function calls (where commas separate arguments instead of forming tuples).
So, overall, there is no need for iterator unpacking in indexing. Simply convert your iterator to a tuple use it as the index.
Upvotes: 2
Reputation: 4585
Referring to your example:
import numpy as np
def lookup(a: np.ndarray, coordinates: tuple) -> float:
return a[*coordinates]
a1 = np.zeros((2, 2))
print(lookup(a1, (0, 1))
a2 = np.zeros(2, 2, 2))
print(lookup(a2, (0, 0, 1))
NumPy already accepts indexing like a[coordinates]
where coordinates
is a tuple, without need for the star operator:
>>> a = np.arange(8).reshape(2, 2, 2)
>>> a[(1, 1, 0)]
6
And if you index with lists, you get a different kind of useful behaviour:
>> a[[1, 1, 0], [0]]
array([[4, 5],
[4, 5],
[0, 1]])
Upvotes: 0