Leon Chang
Leon Chang

Reputation: 681

Python ordering rules for arguments passing in a function call

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

Answers (1)

blue note
blue note

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

Related Questions