Function calls and varagrs
As the docs say:
If syntax appears in a function call
*expression
,expression
must evaluate iterable. Elements from this iterable are treated as if they were optional positional arguments; if there are positional arguments x1, ..., xN and the expression evaluates to the sequence y1, ..., yM, this is equivalent to calling with M + N positional arguments x1, ..., xN, y1, ..., yM.A consequence of this is that although the syntax
*expression
may appear after some of the keyword arguments, it is processed before the keyword arguments ...
Many people are confused that function definitions have similar, sometimes misleading, syntax.
In a function definition, a parameter-parameter parameter (for example, *args
) appears before any parameters for a keyword only. Of course, the keyword and the default are completely independent, but it's quite common that all keyword-only parameters have default values. Thus, the syntax often looks like def func(a, *args, c=4, **kwargs):
. This can lead to what func(1, *(2,), c=3, **{'d': 4}
is the syntax of the corresponding call, even if it is not. Just remember, which def func(a=1, *args, c, **kwargs)
is perfectly legal and it still makes a
a positional or keyword parameter and a keyword c
only parameter.
If you're wondering how this works specifically in CPython (although other implementations are probably all pretty similar):
The function call itself is compiled to pass a value expression
on the stack, still separated from normal arguments. This is inside the interpreter, in the function call evaluator, where a stack frame is created to execute the function body, where this value is inserted into additional arguments.
This can help you see how CPython parses and compiles this code:
>>> astpp(ast.parse("func(1, c=3, *(2,), **{'d':4})"))
Module(
body=[
Expr(
value=Call(
func=Name(id='func', ctx=Load()),
args=[Num(n=1)],
keywords=[keyword(arg='c', value=Num(n=3))],
starargs=Tuple(elts=[Num(n=2)], ctx=Load()),
kwargs=Dict(keys=[Str(s='d')], values=[Num(n=4)])))])"
Even if you don't understand the AST, you should be able to see what (2,)
is still separate during parsing, stored in the named field starargs
.
This will compile to this bytecode:
2 0 LOAD_GLOBAL 0 (func)
3 LOAD_CONST 1 (1)
6 LOAD_CONST 2 ('c')
9 LOAD_CONST 3 (3)
12 LOAD_CONST 7 ((2,))
15 BUILD_MAP 1
18 LOAD_CONST 5 (4)
21 LOAD_CONST 6 ('d')
24 STORE_MAP
25 CALL_FUNCTION_VAR_KW 257
28 POP_TOP
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
You probably don't understand all this gibberish, but you can see that the tuple is (2,)
loaded onto the stack at offset 12, and it is still on the stack when the opcode CALL_FUNCTION_VAR_KW
is executed. And you can look at this opcode in the docs where it says:
Calls a function.
argc
interpreted asCALL_FUNCTION
. The top item on the stack contains a keyword argument dictionary, followed by a tuple of argument variables, followed by explicit keywords and positional arguments.
So the "argument-variable tuple" is still separate.
source to share
Positional parameters should always appear before named and unpacked parameters.
In the expression:
func(1, c=3, 2, **{'d':4})
2
is a positional parameter, and c=3
is a named parameter. It is incorrectly written like this. You must move the named parameter after all positional parameters.
func(1, 2, c=3, **{'d':4})
On the other hand, the expression:
func(1, c=3, *(2,), **{'d':4})
... 1
is the only positional parameter here. c=3
is a named parameter, and *(2,)
and are **{'d':4}
unpacked. All this is valid as long as the positioning parameter is the first.
source to share