3. 和 4. 应该在所有 Python 版本上都存在语法错误。 但是您发现了一个影响 Python 2.5 - 3.4 版本的错误,该错误是随后posted to the Python issue tracker。由于该错误,如果仅附有 *args 和/或 **kwargs,则未加括号的生成器表达式被接受为函数的参数。虽然 Python 2.6+ 允许 case 3. 和 case 4.,但 Python 2.5 只允许 case 3. - 但它们都反对documented grammar:
call ::= primary "(" [argument_list [","]
| expression genexpr_for] ")"
即文档说函数调用包含primary(计算结果为可调用的表达式),然后在括号中,要么参数列表或只是一个无括号的生成器表达;
并且在参数列表中,所有生成器表达式都必须在括号中。
这个错误(虽然它似乎不为人所知)已在 Python 3.5 预发布版本中修复。在 Python 3.5 中,生成器表达式始终需要括号,除非它是函数的唯一参数:
Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(1 for i in [42], *a)
File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument
感谢 DeTeReR 发现了此错误,这已记录在 What's New in Python 3.5 中。
漏洞分析
对 Python 2.6 进行了更改,allowed the use of keyword arguments after *args:
在 *args 之后提供关键字参数也是合法的
函数调用的参数。
>>> def f(*args, **kw):
... print args, kw
...
>>> f(1,2,3, *(4,5,6), keyword=13)
(1, 2, 3, 4, 5, 6) {'keyword': 13}
以前这会是一个语法错误。 (阿莫里供稿
忘记弧;第 3473 期。)
但是,Python 2.6 grammar 不区分关键字参数、位置参数或裸生成器表达式 - 对于解析器来说,它们都是 argument 类型。
根据 Python 规则,如果生成器表达式不是函数的唯一参数,则必须用括号括起来。这在Python/ast.c 中得到验证:
for (i = 0; i < NCH(n); i++) {
node *ch = CHILD(n, i);
if (TYPE(ch) == argument) {
if (NCH(ch) == 1)
nargs++;
else if (TYPE(CHILD(ch, 1)) == gen_for)
ngens++;
else
nkeywords++;
}
}
if (ngens > 1 || (ngens && (nargs || nkeywords))) {
ast_error(n, "Generator expression must be parenthesized "
"if not sole argument");
return NULL;
}
但是这个函数不考虑*args - 它专门只寻找普通的位置参数和关键字参数。
在同一个函数中,还有一个为non-keyword arg after keyword arg生成的错误消息:
if (TYPE(ch) == argument) {
expr_ty e;
if (NCH(ch) == 1) {
if (nkeywords) {
ast_error(CHILD(ch, 0),
"non-keyword arg after keyword arg");
return NULL;
}
...
但这同样适用于不是无括号生成器表达式的参数,如evidenced by the else if statement:
else if (TYPE(CHILD(ch, 1)) == gen_for) {
e = ast_for_genexp(c, ch);
if (!e)
return NULL;
asdl_seq_SET(args, nargs++, e);
}
因此,一个未加括号的生成器表达式被允许通过。
现在在 Python 3.5 中,可以在函数调用的任何地方使用*args,所以
Grammar 已更改以适应这一点:
arglist: argument (',' argument)* [',']
和
argument: ( test [comp_for] |
test '=' test |
'**' test |
'*' test )
和for loop was changed 到
for (i = 0; i < NCH(n); i++) {
node *ch = CHILD(n, i);
if (TYPE(ch) == argument) {
if (NCH(ch) == 1)
nargs++;
else if (TYPE(CHILD(ch, 1)) == comp_for)
ngens++;
else if (TYPE(CHILD(ch, 0)) == STAR)
nargs++;
else
/* TYPE(CHILD(ch, 0)) == DOUBLESTAR or keyword argument */
nkeywords++;
}
}
从而修复错误。
然而无意的变化是看起来有效的结构
func(i for i in [42], *args)
和
func(i for i in [42], **kwargs)
*args 或 **kwargs 之前的无括号生成器现在停止工作。
为了找到这个错误,我尝试了各种 Python 版本。在 2.5 中你会得到 SyntaxError:
Python 2.5.5 (r255:77872, Nov 28 2010, 16:43:48)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
File "<stdin>", line 1
f(*[1], 2 for x in [2])
这在 Python 3.5 的一些预发布之前已修复:
Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument
但是,带括号的生成器表达式,它在 Python 3.5 中有效,但在 Python 3.4 中无效:
f(*[1], (2 for x in [2]))
这就是线索。在 Python 3.5 中,*splatting 是通用的;你可以在函数调用的任何地方使用它:
>>> print(*range(5), 42)
0 1 2 3 4 42
所以实际的错误(生成器使用不带括号的*star)确实在 Python 3.5 中得到修复,并且可以在 Python 3.4 和 3.5 之间的变化中找到错误