Semantics of tuple unpacking in python

Each Answer to this Q is separated by one/two green lines.

Why does python only allow named arguments to follow a tuple unpacking expression in a function call?

>>> def f(a,b,c):
...     print a, b, c
>>> f(*(1,2),3)
  File "<stdin>", line 1
SyntaxError: only named arguments may follow *expression

Is it simply an aesthetic choice, or are there cases where allowing this would lead to some ambiguities?

i am pretty sure that the reason people “naturally” don’t like this is because it makes the meaning of later arguments ambiguous, depending on the length of the interpolated series:

def dangerbaby(a, b, *c):

>>> dangerbaby('puppy', 'bug')
killed bug
>>> cuddles = ['puppy']
>>> dangerbaby(*cuddles, 'bug')
killed bug
>>> cuddles.append('kitten')
>>> dangerbaby(*cuddles, 'bug')
killed kitten

you cannot tell from just looking at the last two calls to dangerbaby which one works as expected and which one kills little kitten fluffykins.

of course, some of this uncertainty is also present when interpolating at the end. but the confusion is constrained to the interpolated sequence – it doesn’t affect other arguments, like bug.

[i made a quick search to see if i could find anything official. it seems that the * prefix for varags was introduced in python 0.9.8. the previous syntax is discussed here and the rules for how it worked were rather complex. since the addition of extra arguments “had to” happen at the end when there was no * marker it seems like that simply carried over. finally there’s a mention here of a long discussion on argument lists that was not by email.]

I suspect that it’s for consistency with the star notation in function definitions, which is after all the model for the star notation in function calls.

In the following definition, the parameter *c will slurp all subsequent non-keyword arguments, so obviously when f is called, the only way to pass a value for d will be as a keyword argument.

def f(a, b, *c, d=1):
    print "slurped", len(c)

(Such “keyword-only parameters” are only supported in Python 3. In Python 2 there is no way to assign values after a starred argument, so the above is illegal.)

So, in a function definition the starred argument must follow all ordinary positional arguments. What you observed is that the same rule has been extended to function calls. This way, the star syntax is consistent for function declarations and function calls.

Another parallelism is that you can only have one (single-)starred argument in a function call. The following is illegal, though one could easily imagine it being allowed.

f(*(1,2), *(3,4))

First of all, it is simple to provide a very similar interface yourself using a wrapper function:

def applylast(func, arglist, *literalargs):
  return func(*(literalargs + arglist))

applylast(f, (1, 2), 3)  # equivalent to f(3, 1, 2)

Secondly, enhancing the interpreter to support your syntax natively might add overhead to the very performance-critical activity of function application. Even if it only requires a few extra instructions in compiled code, due to the high usage of those routines, that might constitute an unacceptable performance penalty in exchange for a feature that is not called for all that often and easily accommodated in a user library.

Some observations:

  1. Python processes positional arguments before keyword arguments (f(c=3, *(1, 2)) in your example still prints 1 2 3). This makes sense as (i) most arguments in function calls are positional and (ii) the semantics of a programming language need to be unambiguous (i.e., a choice needs to be made either way on the order in which to process positional and keyword arguments).
  2. If we did have a positional argument to the right in a function call, it would be difficult to define what that means. If we call f(*(1, 2), 3), should that be f(1, 2, 3) or f(3, 1, 2) and why would either choice make more sense than the other?
  3. For an official explanation, PEP 3102 provides a lot of insight on how function definitions work. The star (*) in a function definition indicates the end of position arguments (section Specification). To see why, consider: def g(a, b, *c, d). There’s no way to provide a value for d other than as a keyword argument (positional arguments would be ‘grabbed’ by c).
  4. It’s important to realize what this means: as the star marks the end of positional arguments, that means all positional arguments must be in that position or to the left of it.

change the order:

def f(c,a,b):

If you have a Python 3 keyword-only parameter, like

def f(*a, b=1):

then you might expect something like f(*(1, 2), 3) to set a to (1 , 2) and b to 3, but of course, even if the syntax you want were allowed, it would not, because keyword-only parameters must be keyword-only, like f(*(1, 2), b=3). If it were allowed, I suppose it would have to set a to (1, 2, 3) and leave b as the default 1. So it’s perhaps not syntactic ambiguity so much as ambiguity in what is expected, which is something Python greatly tries to avoid.

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .