77

Why can't you use a trailing comma with *args in Python? In other words, this works

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

But this does not

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

This is the case with both Python 2 and Python 3.

Kevin J. Chase
  • 3,856
  • 4
  • 21
  • 43
asmeurer
  • 86,894
  • 26
  • 169
  • 240
  • 4
    Something to do with the way `(1,)` creates a tuple maybe. – TankorSmash Jun 05 '13 at 21:47
  • 3
    Apparently the keyword args are irrelevant. `f(*args,)` is also disallowed. – asmeurer Jun 05 '13 at 21:48
  • @TankorSmash But it's not a tuple here as the `(` doesn't appear in an expression location. – user2246674 Jun 05 '13 at 21:48
  • @user2246674 As CPython's parser is autogenerated from the grammar, that much is obvious; the question is **why** this is disallowed by the grammar. My take is that `f(*args,)` doesn't make sense because the user of `*args` precludes arbitrary additional positional arguments, which is normally the rationale for allowing the trailing comma. However, since Python supports keyword arguments, disallowing the trailing comma looks like an oversight. – user4815162342 Jun 05 '13 at 21:49
  • @user4815162342 The "why" is entirely irrelevant here. Either way, it was a design decision. The only interesting objective question is related to the difference(s) between the specification and the implementation - or how to correctly understand the specification (independent of "why"). – user2246674 Jun 05 '13 at 21:51
  • 11
    @user2246674 The "why" is far from irrelevant — it is precisely the point of the question. Grammars of languages like Python are carefully crafted, with a lot of consideration being invested in readability, ease of use, ease of parsing, backward compatibility, etc. There is very likely a reason why trailing comma is allowed everywhere *except* after `*args`, and it is legitimate to be curious about that reason. If you disagree, well, there are many other questions in need of answer. – user4815162342 Jun 05 '13 at 21:56
  • 1
    @user4815162342: I disagree. I think the specification could use some work here. `f(x=1, *(2, 3))` is allowed, but `f(x=1, 2, 3)` is not. `f(1, 2, x=3,)` is allowed, but `f(*(1, 2), x=3,)` is not. – Bill Lynch Jun 05 '13 at 22:53
  • 2
    @sharth Since your own answer shows that the implementation doesn't correspond to the specification, one might as well say that it's the implementation that should be fixed. Either way, that is orthogonal to the legitimacy of asking the question why the current behavior is as it is. The *answer* to that question might as well be that it's a simple oversight (as mentioned in my first comment) or an implementation bug — Python grammar may be carefully crafted, but no one is perfect. – user4815162342 Jun 06 '13 at 07:11

2 Answers2

105

Let's look at the language specification:

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

Let's sift down to the parts we care about:

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

So, it looks like after any arguments to a function call, we're allowed an extra ,. So this looks like a bug in the cpython implementation.

Something like: f(1, *(2,3,4), ) should work according to this grammar, but doesn't in CPython.


In an earlier answer, Eric linked to the CPython grammar specification, which includes the CPython implementation of the above grammar. Here it is below:

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

Note, that this grammar is not the same as the one proposed by the language specification. I'd consider this an implementation bug.


Note that there are additional issues with the CPython implementation. This should also be supported: f(*(1,2,3), *(4,5,6))

Oddly though, the specification does not allow f(*(1,2,3), *(4,5,6), *(7,8,9))

As I look at this more, I think this part of the specification needs some fixing. This is allowed: f(x=1, *(2,3)), but this isn't: f(x=1, 2, 3).


And to perhaps be helpful to the original question, in CPython, you can have a trailing comma if you don't use the *args or the **kwargs feature. I agree that this is lame.

Community
  • 1
  • 1
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
  • 17
    Got to upvote this. This is the first time I've seen someone pull out Python's language specification to support an answer. – lightalchemist Jun 06 '13 at 01:11
  • Good answer in general. But I'm not sure what you mean when you say that `f(*(1,2,3), *(4,5,6))` "should be supported" -- it looks to me like neither grammar (the language spec and the cpython grammar) would allow for that. – Edward Loper Jun 06 '13 at 16:19
  • 2
    @EdwardLoper Probably referring to the line of the spec grammar that says `| "*" expression ["," "*" expression] ["," "**" expression]`. The first clause in square brackets looks like it got there by mistake, the line should be `| "*" expression ["," "**" expression]`. As sharth says, the specification does need some work. – user4815162342 Jun 06 '13 at 17:59
  • @EdwardLoper: That's the correct line that I was referring to. It does seem like a mistake to me as well. – Bill Lynch Jun 06 '13 at 19:54
  • 6
    This issue was submitted on 2010-12-12 into python bug tracker and was rejected: http://bugs.python.org/issue10682 – Aliaksei Ramanau Jun 12 '13 at 08:56
  • 2
    @dr.: Thanks for finding something in the tracker about this. I hadn't had a chance to look around. It looks like the bug you've referenced should have been closed as a duplicate of this one: http://bugs.python.org/issue9232, which they mark as having superseded 10682. – Bill Lynch Jun 12 '13 at 13:33
7

After some discussion regarding this bug in issue 9232, Guido van Rossum commented:

I'm +1 on adding this. I don't believe it requires a PEP. A trailing comma in definitions is already supported in some places, so I don't buy the argument that it catches errors. During the moratorium we were perhaps too strict.

Subsequently, a patch by Mark Dickinson was committed. So this is now fixed in Python 3.6.0 alpha 1.

BioGeek
  • 21,897
  • 23
  • 83
  • 145
  • 1
    That's awesome! I originally hit this in actual code and it took me some time to figure out why Python was telling me I had a syntax error. – asmeurer Sep 28 '15 at 16:31