【问题标题】:Tuple unpacking while indexing索引时元组解包
【发布时间】:2019-12-20 12:41:58
【问题描述】:

这行得通:

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

但这不是:

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

如何在索引时解包元组?

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

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

【问题讨论】:

  • 在你的第二个 sn-p y 没有作为变量引入,所以 y[*x] 没有任何意义,因此语法错误。
  • 您希望索引如何与['foo', 'bar'] 一起工作?索引需要一个单独的整数,而不是字符串列表
  • 你能举一个例子吗?
  • @quamrana:不,缺少变量声明会引发NameError,而不是SyntaxError。试试not_declared[0]
  • @Chris_Rands:对于可能是真的列表。但是 NumPy 数组、Pandas DataFrames 和typing 泛型具有更复杂的索引接口,并且支持foo[a, b, c] 之类的东西。请参阅我的示例。

标签: python iterable-unpacking


【解决方案1】:

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

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

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)

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

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

【讨论】:

    【解决方案2】:

    参考你的例子:

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

    【讨论】:

    • 感谢您的意见。正如我所写的那样,我不太关心让示例正常工作,而更关心为什么我的方法不起作用(尽管我对我的 typing 示例的解决方法感兴趣)。
    【解决方案3】:
    x = ['foo', 'bar']
    y[*x]  # raises SyntaxError (not NameError!)
    

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

    type(*x)
    

    这会返回错误

    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
    

    【讨论】:

    • type(*x) 的错误是因为 x 的元素数量错误。函数参数中的迭代器解包适用于 typetype(*[1]) 返回 int。你的第二个例子的第一部分是我要问的(你在这里没有提供解释),你的第二个例子的第二部分缺少逗号。
    • type(*['foo','bar']) 或 type(*[1,2]) 会给你我指定的错误。此外,没有缺少逗号,但 *x 本身打印 foo bar 而不是 ['foo', 'bar'] 除非我误解了你。
    • 是的,type(*['foo', 'bar']) 也会给出同样的错误,但我看不出这与索引操作不支持元组解包语法有什么关系。正如我所说,解包在函数调用中得到很好的支持。关于缺少的逗号:我看不出y[foo bar] 应该如何是有效的Python 语法以及它与解包有什么关系——您的评论this is y[*x] 没有多大意义,因为y[*x] 没有定义(这就是我的问题的全部内容),如果它被定义,它肯定不会扩展到像 y[foo bar] 这样在语法上无效的东西。
    • y = [*x],我从你的问题中提到,y[*x] 给你语法错误(根据问题),所以我的全部解释是 y[*x]会给你语法错误,你得到了,因为访问 y[*x] 正在访问 y[foo bar],这在语法上是不正确的。
    • 我认为“y[*x] 正在访问y[foo bar]”没有任何意义。 “访问”应该是什么意思?正如您从my answer 看到的那样,这是一个问题,即y[x] 如何精确地脱糖到type(y).__getitem__(y, x) 以及在[] 之间预期什么样的语法实体(例如,表达式与参数列表) .
    猜你喜欢
    • 2019-11-24
    • 1970-01-01
    • 2021-12-24
    • 2020-12-25
    • 2019-04-21
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-27
    相关资源
    最近更新 更多