方括号内的星号表达式

5
似乎星号表达式不能像括号内那样在方括号内使用:
>>> import numpy as np
>>> x = np.ones((3, 4))
>>> x[:, *(None, None), :]
  File "<stdin>", line 1
    x[:, *(None, None), :]
         ^
SyntaxError: invalid syntax

虽然人们可能期望后者输出与前者相同。
>>> x[(slice(None), *(None, None), slice(None))]
array([[[[ 1.,  1.,  1.,  1.]]],


       [[[ 1.,  1.,  1.,  1.]]],


       [[[ 1.,  1.,  1.,  1.]]]])

您知道为什么不允许这样做的好理由吗?如果在未来的Python版本中有支持计划吗?


[*] 只会在 numpy 中使用。普通的 Python 索引只允许一个值。(*) 在许多地方都会出现,有时没有括号。 - hpaulj
常规的Python索引实际上允许多个输入。它们被收集在一个元组中,然后传递给容器的__getitem__方法。其他库如pandaspytorch等也使用numpy的多索引风格,所以我不会说这只是一个与numpy相关的问题!而且,星号表达式确实可以在没有括号的情况下使用,但是将container[*items]称为function(*args)是否有意义呢? - Roméo Després
那个 __getitem__ 元组是因为 numpy 需要它而添加的。没有任何内置的 Python 对象使用它。我有 pandaspytorch 作为 numpy 的衍生品。你甚至可以编写自己的类来利用这个特性。我提出这一点只是为了争论 [*] 对于核心 Python 来说并没有太多用处。 - hpaulj
元组*扩展用于括号被隐含的情况下。实际上,逗号定义了一个元组而不是()。然而,我不知道足够的正式语法来说解包中*的使用是否与其在函数参数和元组中的使用相同。我怀疑它们都由同一分支处理。 - hpaulj
实际上,在列表中 * 是有效的,例如 [*abc] = 1,2,3; [*abc]。但索引括号不会创建列表。也许我们可以在这里进行头脑风暴,但其实这个问题应该放在 PEP 或 Python 开发者论坛上讨论。 - hpaulj
1个回答

5
关于如何进行操作,答案可能是x[:, None, None, :]。但也许你有一个包含nones = (None, None)的元组,在这种情况下,您可以这样做:x[:, nones[0], nones[1], :]。但我同意,x[:, *nones, :]更好。为什么不可能呢?我们可以查看Python语法来了解为什么它不起作用:
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [',']
subscript: test | [test] ':' [test] [sliceop]
sliceop: ':' [test]

如果你从test开始跟随最长和最有前途的语法分支,搜索一个'*'字面值(我没有呈现整个子树,因为大多数其他分支很早就停止了):test -> or_test -> and_test -> comparison -> xor_expr -> and_expr -> shift_expr -> arith_expr

请注意,我们要搜索的规则是这个:

star_expr: '*' expr

让我们看看能否从这里 (算术表达式) 找到它:

arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
power: atom_expr ['**' factor]
atom_expr: ['await'] atom trailer*

记住,trailer是我们的起点,所以我们已经知道这里没有任何通向star_expr的路径,atom关闭了所有这些不同的路径:

atom: ('(' [yield_expr|testlist_comp] ')' |
       '[' [testlist_comp] ']' |
       '{' [dictorsetmaker] '}' |
       NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')

基本上,你只能使用我们在分支中看到的任何表达式(test -> ... -> arith_expr -> ... -> atom)或任何一个来自NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False'的表达式作为subscriptlist

这里有很多我没有涉及到的分支。对于大多数分支,你可以通过查看规则名称就能理解其中的原因。另一方面,再次检查可能并不无用。

例如,在阅读表达式test时:

test: or_test ['if' or_test 'else' test] | lambdef

我们可以看到,['if' or_test 'else' test](因为我们在寻找* something)结束了路径。另一方面,我本可以包括lambdef,因为它对我们的任务来说完全有效,但是我忽略了这样的路径,因为我们在探索后就会立即关闭它们(应用单个规则使*...无效),在这种情况下(正如您可能已经猜到的那样):
lambdef: 'lambda' [varargslist] ':' test

我们在这里看到了一个'lambda'表达式,它不是以'*'开头的表达式,所以路径关闭了。有趣的是,这意味着x [lambda e:e]是完全合法的语法(我不会猜测并且我从没见过,但这很有意义)。
最后,在整个过程中我们没有看到任何以'*'开头的表达式,因此你提出的方案没有歧义(除非我遗漏了某些规则组合)。也许询问那些实际从事这方面工作的人检查一下是否有不使用潜在语法歧义的好理由。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接