Passing Arguments#

In contrast to several other programming languages Python provides very flexible and readable syntax constructs for passing data to functions. Here we’ll also discuss what happens in memory when passing data to functions.

Positional Arguments#

Positional arguments have to be passed in exactly the same order as they appear in the function’s definition. There can be as many positional arguments as needed. But a function may come without any positional arguments at all, too.

Positional arguments may have a default value, which is used if the argument is missing in a function call. Syntax:

def my_function(arg1=default_value, arg2=default_value2):
    # indented block of code

If there are mandatory arguments (that is, without default value) and optional arguments, then the latter have to follow the former.

Keyword Arguments#

If there are several optional arguments and only some shall be passed to the function, they can be provided as keyword arguments. The order of keyword arguments does not matter when calling a function.

def my_function(arg1=default1, arg2=default2, arg3=default3):
    # indented block of code

my_function(arg3=12345, arg2=54321)

Keyword arguments are very common in Python, since they increase readability when calling functions with many arguments.

Arbitrary Number of Positional Arguments#

If we need a function which can take an arbitrary number of arguments, we may use the following snytax:

def my_function(arg1, arg2, *other_args):
    # indented block of code

Then other_args contains a tuple of all arguments passed to the function, but without arg1 and arg2.

Arbitrary Number of Keyword Arguments#

If we need a function which can take an arbitrary number of keyword arguments, we may use the following snytax:

def my_function(arg1, arg2, kwarg1=default1, kwarg2=default2, **other_kwargs):
    # indented block of code

Then other_kwargs contains a dictionary of all keyword arguments passed to the function, but without kwarg1 and kwarg2.

Argument Unpacking#

If we have a list or tuple and all items shall be passed as single arguments to a function, then we should use argument unpacking:

my_list = [4, 3, 1]
some_function(*my_list)
some_function(my_list[0], my_list[1], my_list[2])

Both calls to some_function are equivalent.

Same works with keyword arguments and dictionaries, where keys are argument names and values are values to be passed to the function.

my_dict = {'kwarg1': 3, 'kwarg2': 5, 'kwarg3': 100}
some_function(**my_dict)

Memory Management#

Passing Mutable Objects#

Python never copies objects passed to a function. Instead, the argument names in a function definition are tied to the objects, whose names are given in the function call.

def some_function(arg1, arg2):
    print(id(arg1), id(arg2))
    
a = 5
b = 'some string'

print(id(a), id(b))

some_function(a, b)
139792935403888 139792871198064
139792935403888 139792871198064

Here we have to take care: if we pass mutable objects to a function, then the function may modify these objects!

def clear_list(l):
    for k in range(0, len(l)):
        l[k] = 0
        
my_list = [2, 5, 3]

clear_list(my_list)

print(my_list)
[0, 0, 0]

Always look up a function’s documentation if you have to pass mutable objects to a function. If the function modifies an object, this fact should be provided in the documentation. For instance, several functions of the OpenCV library, which we’ll use later on, modify their arguments without proper documentation.

Mutable Default Values#

A similar issue arises if we use mutable objects as default values for optional arguments. The name of an optional argument is tied to the object only once during execution (at time of function definition). If this object gets modified, then the default value changes for subsequent function calls.

def append42(l = []):
    l. append(42)
    print(l)
    
append42()
append42([1, 2, 3])
append42()
append42()
[42]
[1, 2, 3, 42]
[42, 42]
[42, 42, 42]

To prevent such problems use a construction similar to the following:

def append42(l = None):
    if l == None:
        l = []
    l. append(42)
    print(l)
    
append42()
append42([1, 2, 3])
append42()
append42()
[42]
[1, 2, 3, 42]
[42]
[42]

Restricting Argument Passing#

We may restrict a function’s arguments to one of the following types:

  • positional only,

  • positional and keyword,

  • keyword only.

For this purpose we have to add / and * to the argument list in a function’s definition:

def my_function(pos1, pos2, /, poskw1, poskw2, *, kw1, kw2):
    # indented block of code

In a call to the function the first group of arguments has to be passed without keyword, the second group may be passed with or without keyword, and the third group has to be passed by keyword.

The reason for existence of this technique is quite involved and, presumably, we won’t need this feature. But we should know it to understand code written by others.

Functions in Python’s Documentation#

Flexibility of argument passing makes it hard to clearly document which variants a library function accepts. Python’s documentation uses a special syntax to state type and number of arguments as well as default values of a function.

Example: The glob module’s glob function (see File IO) is shown in Python’s documentation as follows:

glob.glob(pathname, *, root_dir=None, dir_fd=None, recursive=False)

We see:

  • pathname is the only positional argument.

  • There are three arguments which have to be passed by keyword.

  • pathname is mandatory, whereas the other arguments have default values.

Another example: The built-in input function.

input([prompt])
  • There is only one argument.

  • The argument is optional (indicated by [...]), that is, calling without any arguments is okay.

One more: The built-in print function.

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
  • print accepts an arbitrary number of positional arguments.

  • sep, end, file, flush have to be passed by keyword (because they follow a * argument).

  • The four keyword arguments are optional (have default values).