Look at the following example
point = (1, 2)
size = (2, 3)
color = 'red'
class Rect(object):
def __init__(self, x, y, width, height, color):
pass
It would be very tempting to call:
Rect(*point, *size, color)
Possible workarounds would be:
Rect(point[0], point[1], size[0], size[1], color)
Rect(*(point + size), color=color)
Rect(*(point + size + (color,)))
But why is Rect(*point, *size, color) not allowed, is there any semantic ambiguity or general disadvantage you could think of?
EDIT: Specific Questions
Why are multiple *arg expansions not allowed in function calls?
Why are positional arguments not allowed after *arg expansions?
As far as I know, it was a design choice, but there seems to be a logic behind it.
EDIT: the
*argsnotation in a function call was designed so you could pass in a tuple of variables of an arbitrary length that could change between calls. In that case, having something like f(*a, *b, c) doesn’t make sense as a call, as ifachanges length all the elements of b get assigned to the wrong variables, and c isn’t in the right place either.Keeping the language simple, powerful, and standardized is a good thing. Keeping it in sync with what actually goes on in processing the arguments is also a very good thing.
Think about how the language unpacks your function call. If multiple
*argare allowed in any order likeRect(*point, *size, color), note that all that matters to properly unpack is that point and size have a total of four elements. Sopoint=(),size=(1,2,2,3), andcolor='red')would allowRect(*point, *size, color)to work as a proper call. Basically, the language when it parses the *point and *size is treating it as one combined*argtuple, soRect(*(point + size), color=color)is more faithful representation.There never needs to be two tuples of arguments passed in the form
*args, you can always represent it as one. Since assignment of parameters is only dependent on the order in this combined*arglist, it makes sense to define it as such.If you can make function calls like f(*a, *b), the language almost begs to allow you to define functions with multiple *args in the parameter list, and those couldn’t be processed. E.g.,
How would
f(1,2,3,4)be processed?I think this is why for syntactical concreteness, the language forces function calls and definitions to be in the following specific form; like
f(a,b,x=1,y=2,*args,**kwargs)which is order dependent.Everything there has a specific meaning in a function definition and function call.
aandbare parameters defined without default values, nextxandyare parameters defined with default values (that could be skipped; so come after the no default parameters). Next,*argsis populated as a tuple with all the args filled with the rest of the parameters from a function call that weren’t keyword parameters. This comes after the others, as this could change length, and you don’t want something that could change length between calls to affect assignment of variables. At the end **kwargs takes all the keyword arguments that weren’t defined elsewhere. With these concrete definitions you never need to have multiple*argsor**kwargs.