Reputation: 681
I tried to use * and ** to pass any number of arguments to a function. In "Learning Python" authored by Mark Lutz, it says to follow the order of positional (value) first, then a combination of keyword arguments (name=value) and *sequence, followed by **dict. However, I found that the positional arguments need to come first if present, but the rest of three, to certain extent, can be mixed in order.
Code keywords3.py:
def func(a, b=1, *pArgs, **kwArgs):
print("a = {0}, b = {1}".format(a,b))
print("Positional args = {}".format(pArgs))
print("Keyword args = {}".format(kwArgs))
By trial-and-error,
[1] Between keywords and **dict, they can be in any order...
>>> import keywords3 as j
>>> j.func(b = 3, **{'a':2,'c':4,'d':5})
a = 2, b = 3
Positional args = ()
Keyword args = {'d': 5, 'c': 4}
>>> j.func( **{'a':2}, b = 3, **{'c':4})
a = 2, b = 3
Positional args = ()
Keyword args = {'c': 4}
[2] Between positional args and *sequence, they can be in any order...
>>> j.func(*(2, 3), 4, *(5, 6))
a = 2, b = 3
Positional args = (4, 5, 6)
Keyword args = {}
>>> j.func(2, *(3, 4), 5, *(6,7), **{'c':8})
a = 2, b = 3
Positional args = (4, 5, 6, 7)
Keyword args = {'c': 8}
[3] In general, positional or *sequence arguments need to appear before keyword or **dict arguments.
>>> j.func(*(3, 4), 5, *(6,7), d=15, **{'c':8}, e=16)
a = 3, b = 4
Positional args = (5, 6, 7)
Keyword args = {'e': 16, 'd': 15, 'c': 8}
>>> j.func(d=15, 5, *(6,7), **{'c':8}, e=16)
File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
>>> j.func(**{'a':2}, 5, *(6,7), **{'c':8}, e=16)
File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument unpacking
>>> j.func(**{'a':2}, *(6,7), **{'c':8}, e=16)
File "<stdin>", line 1
SyntaxError: iterable argument unpacking follows keyword argument unpacking
[4] One exception is that iterable argument unpacking *(6,7) following keyword argument is ok...
>>> j.func(f=5, *(6,7), **{'c':8}, e=16)
a = 6, b = 7
Positional args = ()
Keyword args = {'e': 16, 'f': 5, 'c': 8}
Are these observations correct? Please comment.
Upvotes: 1
Views: 9230
Reputation: 29071
There is one single rule consistent with all your examples: positional arguments go before named arguments.
In all your examples, *
and **
are unpacking operators. So, for example, when you write
f(1, *(2,3), 4)
the language gets
f(1,2,3,4)
They are all positional arguments, the language doesn't know the difference. Similarly for the **
operator.
However, when you violate that only rule, eg
j.func(**{'a':2}, 5, *(6,7), **{'c':8}, e=16)
you get an error, becuase, in this example, **{'a':2}
is equivalent to a=2
, which precedes the positional argument 5
.
Upvotes: 3