Ответ 1
Оба 3. и 4. должны быть синтаксическими ошибками во всех версиях Python. Однако вы обнаружили ошибку, которая влияет на версии 2.5-3.4 на Python и которая впоследствии была отправленный в трекер Python. Из-за ошибки выражение без выраженного генератора принималось как аргумент функции, если оно сопровождалось только *args
и/или **kwargs
. Хотя Python 2.6+ допускал оба случая 3. и 4., Python 2.5 разрешал только случай 3. - но оба они были против документированной грамматики:
call ::= primary "(" [argument_list [","]
| expression genexpr_for] ")"
то есть. в документации говорится, что вызов функции состоит из primary
(выражение, которое оценивается вызываемым), а затем в круглых скобках либо список аргументов, либо просто выражение без выраженного генератора;
и в списке аргументов все выражения генератора должны быть в круглых скобках.
Эта ошибка (хотя кажется, что она не была известна) была исправлена в Python 3.5 preleases. В 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
Теперь это описано в What New в Python 3.5, благодаря обнаружению этой ошибки DeTeReR.
Анализ ошибки
В Python 2.6 было внесено изменение, которое разрешило использование аргументов ключевого слова после *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}
Раньше это была бы синтаксическая ошибка. (Внесено Amaury Forgeot dArc; вопрос 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 после ключевого слова 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;
}
...
Но это опять-таки относится к аргументам, которые не являются явно выраженными выражениями генератора как о чем свидетельствует оператор else if
:
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
был изменен на
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