Parameter Types in Python Functions

This post covers the use of different types of parameters in functions.

Table of Contents

For more information about argument types, see Argument Types in Python Functions.


Parameter Types and Argument Usage


positional-or-keyword parameters

This is a common parameter type in Python functions. If there are no special characters (/, *, **) in the function signature, we can use both positional and keyword arguments.

def func(param1, param2): # function signature with parameters
    pass
    
# function call with positional arguments
func("value", "other value")

# function call with keyword arguments
func(param1="value", param2="other value")

# or both
func("value", param2="other value")

positional-only parameters

This feature has been available since Python 3.8.

The function accepts only positional arguments. All arguments that match the parameters specified before the slash must be positional arguments.

def func(param, /):
    pass

# function call with positional argument
func("value")

Often, positional-only parameters are used when the parameter names don't matter.

def func(x, y, /):
    return x + y
    
func(2, 3)

PEP 570 explains in detail why this type of parameter was added to Python functions.


keyword-only parameters

The function accepts arguments in the form of param=value pairs. All arguments that match parameters specified after an asterisk (*) must be keyword arguments.

def func(*, param):
    pass

# function call with keyword argument
func(param="value")

If the order in which the arguments are passed or their position matters to the logic of the function, then it is better to use the keyword arguments.

def devide(*, dividend, divisor):
    return dividend / divisor

# Regardless of the order in which the arguments are passed,
# we will get the correct result: 2 divided by 3, not 3 divided by 2.
devide(dividend=2, divisor=3)
devide(divisor=3, dividend=2)

var-positional parameters

The function can accept an arbitrary sequence of positional arguments. Inside the function, args is used as a tuple.

def func(*args):
    print(args)

# function call with positional argument
func("value")
# ('value',)

# var-positional parameters can be optional
func()
# ()

var-keyword parameters

The function can take arbitrary keyword arguments. Inside the function, kwargs is used as a dict.

def func(**kwargs):
    print(kwargs)

# function call with keyword argument
func(param="value")
# {'param': 'value'}

# var-keyword parameters can be optional
func()
# {}

*Args and **Kwargs Examples


*args and **kwargs are often used in pairs. In this case, we can use both positional and keyword arguments.

def func(*args, **kwargs):
    print(args, kwargs)
    
func("value")
# ('value',) {}

func(param="other value")
# () {'param': 'other value'}

func("value", param="other value")
# ('value',) {'param': 'other value'}

func()
# () {}

These parameter types are useful for writing wrappers and decorators. In the following example, we need to pass arbitrary arguments.

from functools import wraps

def exceptions_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            # my_func code runs here
            return func(*args, **kwargs)
        except Exception as e:
            print(f"{e.__class__.__name__}: {e} " \
                  f"in {func.__name__}")
            # do something
    return wrapper
    
@exceptions_decorator
def my_func(a, b):
    return a / b
    
my_func(2, 0)
# ZeroDivisionError: division by zero in my_func


*args and **kwargs can be used in class methods, particularly in the __init__ method. But the next example will show this pair in another method of the class.

import argparse
from inspect import signature

print(signature(argparse.ArgumentParser.add_argument))
# (self, *args, **kwargs)

The argparse module is designed to create a CLI in Python. The add_argument method of the ArgumentParser class is for adding command-line program features.

The first values accepted in the method are names or flags. These are arbitrary values that we can choose ourselves (*args). All other values are passed as keyword arguments and correspond to **kwargs.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("foo", help="This is name foo",
                    type=int)
parser.add_argument("-b", "--bar", help="This is flag -b/--bar",
                    action="store_true")

The parser takes values from args and kwargs to generate the CLI options.


add_argument("foo", help="This is name foo", type=int)
The string "foo" is a positional argument passed to the add_argument method.
The help="..." and type=int are keyword arguments passed to the add_argument method.
add_argument("-b", "--bar", help="This is flag -b/--bar", action="store_true")
The strings "-b" and "--bar" are positional arguments passed to the add_argument method.
The help="..." and action="store_true" are keyword arguments passed to the add_argument method.

Although the add_argument method has some restrictions on what you can pass in it, it is also flexible in accepting arguments.


Combinations of Parameters


A general rule for using arguments is that positional arguments precede keyword arguments.

Accordingly, positional-only and var-positional parameters precede keyword-only and var-keyword parameters. Positional-or-keyword parameters are also specified at the beginning of the function signature.

The following conditions must be taken into account:

A positional argument cannot follow a keyword argument in a function call.
A slash (/) must be ahead of an asterisk (*) in a function signature.
An asterisk (*) may appear only once in a function signature, so we cannot use keyword-only parameters and *args at the same time.
**kwargs must be at the end of the function signature.

The longest possible combinations of different types of parameters:

1.(positional-only, /, positional-or-keyword, *, keyword-only, **kwargs)

2.(positional-only, /, positional-or-keyword, *args, **kwargs)

3.(positional-only, /, *args, positional-or-keyword, **kwargs)

In the second example, the positional-or-keyword parameter cannot take a value in the form of a keyword argument because it is followed by *args. Otherwise, we will get a SyntaxError.

In the third example, the positional-or-keyword parameter is forced to be a keyword-only because *args precedes it. Otherwise, we will get a TypeError.

Below are examples of the practical use of different types of parameters.


positional-or-keyword and keyword-only parameters

In some cases, especially if there are many parameters, it is convenient to combine positional and keyword arguments in a function call.

Parameters in such functions often have default values. When calling the function, it is not necessary to pass values to such parameters.

>>> import glob
>>> help(glob.glob)
Help on function glob in module glob:

glob(pathname, *, root_dir=None,
    dir_fd=None, recursive=False, include_hidden=False)
...
>>> glob.glob("*.txt")
['LICENSE.txt', 'NEWS.txt']

positional-only and keyword-only parameters

This example is similar to the previous one. There is also a main positional argument here. Values for key and reverse are optional.

>>> help(sorted)
Help on built-in function sorted in module builtins:

sorted(iterable, /, *, key=None, reverse=False)
...
>>> sorted([2, 1, 3])
[1, 2, 3]
>>> sorted(["giraffe", "lion", "python"], key=len)
['lion', 'python', 'giraffe']

var-positonal parameters

If *args precedes the rest of the parameters, they become keyword-only parameters. To distinguish args from the values of other parameters, we are forced to use keyword arguments for those parameters.

The following function converts all non-keyword arguments to strings and prints them.

>>> help(print)
Help on built-in function print in module builtins:

print(*args, sep=' ', end='\n', file=None, flush=False)
...
>>> print("print is a", print, "in module", print.__module__)
print is a <built-in function print> in module builtins
>>> print(1, 2, 3, sep="-")
1-2-3

Popular posts from this blog

Tkinter Button Widget