索引时元组解包

Tuple unpacking while indexing

这个有效:

x = ['foo', 'bar']
y = [*x]
print(y)  # prints ['foo', 'bar']

但这不是:

x = ['foo', 'bar']
y[*x]  # raises SyntaxError (not NameError!)

如何在索引时解压元组?

这里有两个我想使用这种方法的示例,但我更想了解为什么 *- 解包似乎在一般索引中不受支持。

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

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:
    ...

参考你的例子:

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 已经接受像 a[coordinates] 这样的索引,其中 coordinates 是一个元组,不需要星号运算符:

>>> a = np.arange(8).reshape(2, 2, 2)
>>> a[(1, 1, 0)]
6

如果您使用列表进行索引,您会得到一种不同的有用行为:

>> a[[1, 1, 0], [0]]
array([[4, 5],
       [4, 5],
       [0, 1]])

Python的索引已经内置了对一般元组的支持(不仅仅是NumPy),所以这里不需要拆包。

一般来说,对于type(foo).__getitem__(foo, x)foo[x]syntactic sugar。让我们详细看看它是如何工作的:

class Foo: 
    def __getitem__(self, key): 
        print(repr(key)) 

foo = Foo()

如果我们用单个值索引到 foo,那么它会原封不动地传递给 __getitem__,无论它是标量、列表还是元组:

foo[0]       # prints 0
foo[(0, 1)]  # prints (0, 1)
foo[[0, 1]]  # prints [0, 1]

有趣的情况是当我们在索引时直接提供多个值时会发生什么(没有将它们包装在元组或列表中):

foo[0, 1]  # prints (0, 1)

所以多个值自动包装在一个元组中! foo无法区分foo[0, 1]foo[(0, 1)]。这是因为在 Python grammar 中,索引是一个表达式(或切片,但这里不适用)——而在表达式中,一个 , 形成一个元组:

x = 1, 2
print(repr(x))  # prints (1, 2)

因此,索引中的参数解析与函数调用不同(逗号分隔参数而不是形成元组)。

所以,总的来说,在索引中不需要迭代器解包。只需将迭代器转换为元组,将其用作索引。

x = ['foo', 'bar']
y[*x]  # raises SyntaxError (not NameError!)

这会引发语法错误,因为 * 和 x 被视为两个独立的事物,而不是一个。 例如:如果我执行以下操作

type(*x)

这个returns错误

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type() takes 1 or 3 arguments

这意味着 *x 不被视为一个单独的实体,而是两个独立的实体。

此外,

>>> 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