Python function parameters

Order of positional-or-keyword parameters, *args and **kwargs

Vikash Kumar

2 minute read

Python function parameters ordering *args and **kwargs

Recently I was reviewing a piece of code, where a developer wrote a function which has named parameters, default parameters, *args and **kwargs. In first glance, the code doesn’t look to have any issue but looking carefully to the function signature the issue revealed itself.

Before proceeding, let’s see different type of parameters of a python function.

  • positional-or-keyword - specifies an argument that can be passed either positionally or as a keyword argument.

    def func(foo, bar=None): ...
     - foo - positional parameter (required)
     - bar - keyword parameter    (optional)
    
  • *args - single-aestrik variable used to pass parameter for variable length parameter lists to function.

    def func(foo, *args): ...
      - *args - arbritrary sequence of positional arguments can be
        provided.
    
  • **kwargs - double-aestrik variable used to pass variable length key-value parameters to function.

    def func(*args, **kwargs): ...
        - **kwargs - specifies that arbitrarily many keyword arguments can
            be provided.
    

Let’s examine the function (not the original one)

file.py
-------
def func(ids, *args, namespace=None): ...

$ python3 file.py

$ python2 file.py
File "file.py", line 225
    def func(ids, *args, namespace=None):
                                 ^
SyntaxError: invalid syntax

What happened ? Same function, works fine with python3.x but fails to execute in python2.x. namespace=None is a keyword argument or default argument. In python2.x, the function definition click here, look at the parameter_list in function definition

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

while in python 3.x click here for complete function definition

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

Function definition in both the python version differs in defparameter, where with python 3.x there is flexibility of ordering betweeen default parameter and aestrik-parameter and in python 2.x there is defparameter, aestrik-parameter followed-by double-aestrik parameter. This is the reason, above code failed in python2 but passed in python3.

Below illustrated function syntax works with both python 2.x & 3.x version and hence can be used safely.

    def func(positional_arg, default_arg=None, *args, **kwargs): ...

References:

  1. https://docs.python.org/2/reference/compound_stmts.html#function-definitions
  2. https://docs.python.org/release/3.2.2/reference/compound_stmts.html#function-definitions
comments powered by Disqus