mgilson mgilson - 28 days ago 12
Python Question

Calling a Python function with *args,**kwargs and optional / default arguments

In python, I can define a function as follows:

def func(kw1=None,kw2=None,**kwargs):
...


In this case, i can call func as:

func(kw1=3,kw2=4,who_knows_if_this_will_be_used=7,more_kwargs=Ellipsis)


I can also define a function as:

def func(arg1,arg2,*args):
...


which can be called as

func(3,4,additional,arguments,go,here,Ellipsis)


Finally, I can combine the two forms

def func(arg1,arg2,*args,**kwargs):
...


But, what does not work is calling:

func(arg1,arg2,*args,kw1=None,kw2=None,**kwargs): #SYNTAX ERROR (in python 2 only, apparently this works in python 3)
...


My original thought was that this was probably because a function

def func(arg1,arg2,*args,kw1=None):
...


can be called as

func(1,2,3) #kw1 will be assigned 3


So this would introduce some ambiguity as to whether 3 should be packed into args or kwargs. However, with python 3, there is the ability to specify keyword only arguments:

def func(a,b,*,kw=None): #can be called as func(1,2), func(1,2,kw=3), but NOT func(1,2,3)
...


With this, it seems that there is no syntactic ambiguity with:

def func(a,b,*args,*,kw1=None,**kwargs):
...


However, this still brings up a syntax error (tested with Python3.2). Is there a reason for this that I am missing? And, is there a way to get the behavior I described above (Having *args with default arguments) -- I know I can simulate that behavior by manipulating the kwargs dictionary inside the function.

agf agf
Answer

You can do that on Python 3.

def func(a,b,*args,kw1=None,**kwargs):

The bare * is only used when you want to specify keyword only arguments without accepting a variable number of positional arguments with *args. You don't use two *s.

To quote from the grammar, in Python 2, you have

parameter_list ::=  (defparameter ",")*
                    (  "*" identifier [, "**" identifier]
                    | "**" identifier
                    | defparameter [","] )

while on Python 3, you have

parameter_list ::=  (defparameter ",")*
                    (  "*" [parameter] ("," defparameter)*
                    [, "**" parameter]
                    | "**" parameter
                    | defparameter [","] )

which includes a provision for additional parameters after the * parameter.