Почему конечная запятая имеет значение SyntaxError в списке аргументов, в котором используется синтаксис * args?

Почему вы не можете использовать конечную запятую с *args в Python? Другими словами, это работает

>>> f(1, 2, b=4,)

Но это не

>>> f(*(1, 2), b=4,)
  File "<stdin>", line 1
    f(*(1, 2), b=4,)
                   ^
SyntaxError: invalid syntax

Это относится как к Python 2, так и к Python 3.

Ответ 1

Посмотрите на спецификацию языка:

call                 ::=  primary "(" [argument_list [","]
                          | expression genexpr_for] ")"
argument_list        ::=  positional_arguments ["," keyword_arguments]
                            ["," "*" expression] ["," keyword_arguments]
                            ["," "**" expression]
                          | keyword_arguments ["," "*" expression]
                            ["," "**" expression]
                          | "*" expression ["," "*" expression] ["," "**" expression]
                          | "**" expression
positional_arguments ::=  expression ("," expression)*
keyword_arguments    ::=  keyword_item ("," keyword_item)*
keyword_item         ::=  identifier "=" expression

Позвольте просеять до тех частей, о которых мы заботимся:

call                 ::=  primary "(" [argument_list [","]] ")"
argument_list        ::=  positional_arguments ["," keyword_arguments]
                            ["," "*" expression] ["," keyword_arguments]
                            ["," "**" expression]
positional_arguments ::=  expression ("," expression)*
keyword_arguments    ::=  keyword_item ("," keyword_item)*
keyword_item         ::=  identifier "=" expression

Итак, похоже, что после любых аргументов вызова функции нам разрешен дополнительный ,. Таким образом, это похоже на ошибку в реализации cpython.

Что-то вроде: f(1, *(2,3,4), ) должно работать в соответствии с этой грамматикой, но не в CPython.


В более раннем ответе Eric связан с спецификацией грамматики CPython, который включает реализацию CPython указанной выше грамматики. Здесь он находится ниже:

arglist: (argument ',')* ( argument [',']
                         | '*' test (',' argument)* [',' '**' test] 
                         | '**' test
                         )

Обратите внимание, что эта грамматика не совпадает с как таковая, предложенная спецификацией языка. Я бы счел это ошибкой реализации.


Обратите внимание, что есть дополнительные проблемы с реализацией CPython. Это также должно поддерживаться: f(*(1,2,3), *(4,5,6))

Как ни странно, спецификация не позволяет f(*(1,2,3), *(4,5,6), *(7,8,9))

Поскольку я смотрю на это больше,, мне кажется, что эта часть спецификации нуждается в некоторой фиксации. Это разрешено: f(x=1, *(2,3)), но это не так: f(x=1, 2, 3).


И, возможно, полезный исходный вопрос, в CPython, вы можете иметь конечную запятую, если вы не используете функцию *args или **kwargs. Я согласен, что это хромает.

Ответ 2

После некоторого обсуждения этой ошибки в issue 9232, Guido van Rossum прокомментировал:

Я добавил +1. Я не считаю, что это требует ОПТОСОЗ. Задняя запятая в определениях уже поддерживается в некоторых местах, поэтому я не покупаю аргумент, что он ловит ошибки. Во время моратория мы были, возможно, слишком строгими.

Впоследствии был сделан патч от Марка Дикинсона. Итак, теперь это исправлено в Python 3.6.0 alpha 1.