So far, all functions have used parameter syntax that allows for positional arguments, which is the most straightforward way to pass arguments to a function. However, positional arguments are also the least versatile option for passing arguments, since the arguments and parameters must agree in order and number. Let's take a look at some techniques that relax these restrictions.
#\color{#3e999f}{\mathtt{>>>}}\textit{ name}(\textit{keyword}_\textit{1}\color{#3e999f}{\mathtt{\text{=}}}\textit{value}_\textit{1}, \textit{keyword}_\textit{2}\color{#3e999f}{\mathtt{\text{=}}}\textit{value}_\textit{2}, \dots)#
When calling a function, we can use keyword arguments in the form of keyword #\color{#3e999f}{\mathtt{\text{=}}}#value, to be more explicit and to circumvent the required order of arguments.
Let's use the function #\mathtt{\text{f}}# as example.
def f(months, employee, salary): print(f"{employee} has made ${months * salary} over {months} months.")
|
When calling #\mathtt{\text{f}}# with positional arguments, we need to pass our arguments to #\mathtt{\text{f}}# in the correct order, not doing so might result in an error or unexpected behaviour.
>>> f(6, "Bob", 5000)
|
Bob has made $30000 over 6 months.
|
>>> f(5000, 6, "Bob")
|
5000 has made $BobBobBobBobBobBob over 6 months.
|
As mentioned, we also need to match the number of parameters defined, if we were to call #\mathtt{\text{f}}# with two arguments it will result in an error.
>>> f(6, "Bob")
|
TypeError: f() missing 1 required positional argument: 'salary'
|
By using keyword arguments we are able to pass arguments to #\mathtt{\text{f}}# in any order. However, it is still necessary to match the number of parameters.
>>> f(salary=5000, months=6, employee="Bob")
|
Bob has made $30000 over 6 months.
|
It is also possible to combine positional and keyword arguments in a single function call, when doing so, the positional arguments should always come first and in the correct order.
>>> f(6, salary=5000, employee="Bob")
|
Bob has made $30000 over 6 months.
|
#\begin{split}\color{#8959A8}{\mathtt{def}}\text{ } & \textit{name}(\dots, \color{#3e999f}{\mathtt{/}}, \dots)\mathtt{:}\\&\dots\\\end{split}#
If desired, you can force the use of positional arguments for certain parameters, this can be accomplished using a single slash #\color{#3e999f}{\mathtt{\text{/}}}# as a parameter in the function definition, every parameter before #\color{#3e999f}{\mathtt{\text{/}}}# will only accept positional arguments.
def f(x, y, /, z): print("success")
|
Let's see:
>>> f(x=1, y=2, z=3)
|
TypeError: f() got some positional-only arguments passed as keyword arguments: 'x, y'
|
#\begin{split}\color{#8959A8}{\mathtt{def}}\text{ } & \textit{name}(\dots, \color{#3e999f}{\mathtt{*}}, \dots)\mathtt{:}\\&\dots\\\end{split}#
Similar to positional-only arguments, you can force the use of keyword arguments for certain parameters, this can be accomplished using a single asterisk #\color{#3e999f}{\mathtt{\text{*}}}# as a parameter in the function definition, every parameter after #\color{#3e999f}{\mathtt{\text{*}}}# will only accept keyword arguments.
def f(x, y, *, z): print("success")
|
Let's see:
>>> f(1, 2, 3)
|
TypeError: f() takes 2 positional arguments but 3 were given
|
>>> f(x=1, y=2, z=3)
|
success
|
If you would like to force both positional and keyword arguments you can use both the slash and the asterisk as parameters in the function definition, in that order.
If we use the syntax keyword #\color{#3e999f}{\mathtt{\text{=}}}#value for keyword arguments, but in a function definition, then value becomes a default value for the parameter keyword. Parameters defined this way are referred to as default or optional parameters. Such a definition allows you to omit arguments in a function call.
#\begin{split}\color{#8959A8}{\mathtt{def}}\text{ } & \textit{name}(\textit{keyword}_\textit{1}\color{#3e999f}{\mathtt{\text{=}}}\textit{value}_\textit{1}, \textit{keyword}_\textit{2}\color{#3e999f}{\mathtt{\text{=}}}\textit{value}_\textit{2}, \dots)\mathtt{:}\\&\dots\\\end{split}#
When defining a function we can specify default values for certain parameters. Arguments that correspond to parameters with a default value can be omitted in a function call.
Let's use #\mathtt{\text{f}}# again, but now we define a default value for salary.
def f(months, employee, salary=3000): print(f"{employee} has made ${months * salary} over {months} months.")
|
We're able to call #\mathtt{\text{f}}# with only two arguments, since #\mathtt{\text{salary}}# has a default value now.
>>> f(6, "Bob")
|
Bob has made $18000 over 6 months.
|
If we want a value that differs from the default we can use either a positional or keyword argument to achieve this, each having their respective advantages and disadvantages mentioned.
>>> f(6, "Bob", 6000)
|
Bob has made $36000 over 6 months.
|
>>> f(6, salary=6000, employee="Bob")
|
Bob has made $36000 over 6 months.
|
It may occur that you want to define a function that is able to take a variable amount of arguments. Python provides argument tuple packing for this, it allows a function to take a variable number of arguments while only specifying a single parameter in the function's definition. All arguments will be packed into a single iterable sharing the parameter's name.
#\begin{split}\color{#8959A8}{\mathtt{def}}\text{ } & \textit{name}(\color{#3e999f}{\mathtt{\text{*}} }\mathtt{\text{args}})\mathtt{:}\\&\dots\\\end{split}#
The #\mathtt{\text{args}}# parameter preceded by an asterisk #\color{#3e999f} {\mathtt{\text{*}}}# indicates that all provided arguments in a function call are packed into the iterable #\mathtt{\text{args}}#.
In the function definition, #\mathtt{\text{args}}# can be used as any other iterable.
def f(*args): for arg in args: print(arg)
|
We defined a simple function #\mathtt{\text{f}}# that takes a variable number of arguments and simply prints them.
>>> f("one", 2, "three", 4)
|
one 2 three 4
|
Note that, when you only specify #\mathtt{\color{#3e999f}{\text{*}}\text{args}}# your function can only take positional arguments.
>>> f(one="one")
|
TypeError: f() got an unexpected keyword argument 'one'
|
The downside of this syntax is that parameter names are lost, now it's not possible to discriminate between any of the arguments and perform specific actions on a subset. Luckily, Python also provides a keyword variant for variable arguments called argument dictionary packing. This packs all keyword value pairs into a single iterable.
#\begin{split}\color{#8959A8}{\mathtt{def}}\text{ } & \textit{name}(\color{#3e999f}{\mathtt{\text{**}} }\mathtt{\text{kwargs}})\mathtt{:}\\&\dots\\\end{split}#
The #\mathtt{\text{kwargs}}# parameter preceded by two asterisks #\color{#3e999f} {\mathtt{\text{**}}}# indicates that all provided keyword arguments in a function call are packed into the iterable #\mathtt{\text{kwargs}}# as keyword-value pairs.
In the function definition, #\mathtt{\text{kwargs}}# functions a little different than other iterables, the pairs are actually stored in a dictionary, a datastructure we will learn more about next chapter. When looping through the pairs we use the #\mathtt{\color{#4271ae}{\text{items}}\text{()}}# method to get a pair-by-pair iterable.
def f(**kwargs): for key, value in kwargs.items() print(f"{key} = {value}")
|
We define #\mathtt{\text{f}}# with similar functionality as the previous example, #\mathtt{\text{f}}# now simply prints each keyword-value pair provided as keyword arguments in the function call. The keywords are stored in #\mathtt{\text{kwargs}}# as strings.
>>> f(fruit="apple", price=1.50)
|
fruit = apple price = 1.50
|
Note that, when you only specify #\mathtt{\color{#3e999f}{\text{**}}\text{kwargs}}# your function can only take keyword arguments.
>>> f("apple")
|
TypeError: f() takes 0 positional arguments but 1 was given
|
Naturally, all these techniques can be combined as well. If you were to use all techniques in one function definition, the order of parameters would be: #\mathtt{\text{arg}}, \dots \rightarrow \mathtt{\color{#3e999f}{\text{*}}\text{args}} \rightarrow \mathtt{\text{kwarg}\color{#3e999f}{\text{=}}\color{#F5871F}{\text{None}}}, \dots \rightarrow \mathtt{\color{#3e999f}{\text{**}}\text{kwargs}}#.
Here, we define a function that includes all argument and parameter techniques explained above.
def f(arg, *args, kwarg=None, **kwargs): print(f"arg: {arg}", end=", ") print(f"*args: {args}", end=", ") print(f"kwarg: {kwarg}", end=", ") print(f"**kwargs: {kwargs}")
|
What happens when we call this function with different arguments?
>>> f(1, kwarg=2)
|
arg: 1, *args: (), kwarg: 2, **kwargs: {}
|
Note that, when you specify parameters so that positional or keyword arguments are explicitly required, their values will not be included in the iterables #\mathtt{\text{args}}# and #\mathtt{\text{kwargs}}#.
>>> f(1, 2, 3)
|
arg: 1, *args: (2, 3), kwarg: None, **kwargs: {}
|
>>> f(1, 2, 3, kwarg=1, two=2, three=3)
|
arg: 1, *args: (2, 3), kwarg: 1, **kwargs: {'two': 2, 'three': 3}
|