Keyword arguments are one of those Python features that often seems a little odd for folks moving to Python from many other programming languages. It doesn’t help that folks learning Python often discover the various features of keyword arguments slowly over time.
When teaching Python, I’ve often wished I had a summary of the various keyword argument-related features that I could link learners to. I hope that this article will accomplish that task.
In this article I’m going to explain what keyword arguments are and why they’re used. I’ll then go over some more advanced uses of them that even long-time Python programmers may have overlooked because quite a few things have changed in recent versions of Python 3. If you’re already an experienced Python programmer, you might want to skip to the end.
What are keyword arguments?
Let’s take a look at what keyword arguments (also called “named arguments”) are.
First let’s take this Python function:
1 2 3 4 5 6 |
|
When we call this function, we can pass each of our three arguments in two different ways.
We can pass our arguments as positional arguments like this:
1 2 |
|
Or we can pass our arguments as keyword arguments like this:
1 2 |
|
The order of these arguments matters when they’re passed positionally:
1 2 3 4 |
|
But it doesn’t matter when they’re passed by their name:
1 2 3 4 |
|
When we use keyword/named arguments, it’s the name that matters, not the position:
1 2 3 4 |
|
So unlike many other programming languages, Python knows the names of the arguments our function accepts.
If we ask for help on our function Python will tell us our three arguments by name:
1 2 3 4 |
|
Note that functions can be called with a mix of positional and named arguments:
1 2 |
|
That can come in handy, but with the particular function we’ve written here it’s most clear to use all positional arguments or all keyword arguments.
Why use keyword arguments?
When calling functions in Python, you’ll often have to choose between using keyword arguments or positional arguments. Keyword arguments can often be used to make function calls more explicit.
Take this code:
1 2 3 |
|
This takes a file object output_file
and contents
string and writes a gzipped version of the string to the output file.
This code does the same thing but it uses keyword arguments instead of positional arguments:
1 2 3 |
|
Notice that using this keyword argument call style made it more obvious what each of these three arguments represent.
We were also able to leave off an argument here. The first argument that we left off represents a filename
and already has a default value of None
. We don’t need a filename
here because we’re supposed to pass either a file object or a filename to GzipFile
, not both.
We’re actually able to leave another argument off though.
Here’s the same code again, but the compress level has been left at its default value of 9
this time:
1 2 3 |
|
Because we used named arguments, we were able to leave out two arguments and rearrange the remaining 2 arguments in a sensible order (the file object is more important than the “wt” access mode).
When we use keyword arguments:
- We can often leave out arguments that have default values
- We can rearrange arguments in a way that makes them most readable
- We call arguments by their names to make it more clear what they represent
Where you see keyword arguments
You’ll likely see keyword arguments quite a bit in Python.
Python has a number of functions that take an unlimited number of positional arguments. These functions sometimes have arguments that can be provided to customize their functionality. Those arguments must be provided as named arguments to distinguish them from the unlimited positional arguments.
The built-in print
function accepts the optional sep
, end
, file
, and flush
attributes as keyword-only arguments:
1 2 |
|
The itertools.zip_longest
function also accepts an optional fillvalue
attribute (which defaults to None
) exclusively as a keyword argument:
1 2 3 |
|
In fact, some functions in Python force arguments to be named even when they could have been unambiguously specified positionally.
In Python 2, the sorted
function accepted all its arguments as either positional or keyword arguments:
1 2 3 4 |
|
But Python 3’s sorted
function requires all arguments after the provided iterable to be specified as keyword arguments:
1 2 3 4 5 6 |
|
Keyword arguments come up quite a bit in Python’s built-in functions as well as in the standard library and third party libraries.
Requiring your arguments be named
You can create a function that accepts any number of positional arguments as well as some keyword-only arguments by using the *
operator to capture all the positional arguments and then specify optional keyword-only arguments after the *
capture.
Here’s an example:
1 2 3 4 5 |
|
Note: If you haven’t seen that *
syntax before, *numbers
captures all positional arguments given to the product
function into a tuple which the numbers
variable points to.
The initial
argument in the above function must be specified as a keyword argument:
1 2 3 4 5 6 |
|
Note that while initial
has a default value, you can also specify required keyword-only arguments using this syntax:
1 2 3 4 5 6 7 |
|
That joiner
variable doesn’t have a default value, so it must be specified:
1 2 3 4 5 6 7 8 |
|
Note that this syntax of putting arguments after the *
only works in Python 3. There’s no syntactic way in Python 2 to require an argument to be named.
Keyword-only arguments without positional arguments
What if you want to accept keyword-only arguments without also accepting unlimited positional arguments?
If you want to accept keyword-only arguments and you’re not using a *
to accept any number of positional arguments, you can use a *
without anything after it.
For example here’s a modified version of Django’s django.shortcuts.render
function:
1 2 3 |
|
Unlike Django’s current implementation of render
, this version disallows calling render
by specifying every argument positionally. The content_type
, status
, and using
arguments must be specified by their name
.
1 2 3 4 5 6 |
|
Just like with unlimited positional arguments, these keyword arguments can be required. Here’s a function with four required keyword-only arguments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
This function requires all of its arguments to be specified using their name:
1 2 3 4 5 6 7 8 |
|
Requiring arguments to be named can make calls to our function much clearer.
The purpose of this function call:
1
|
|
Is much more obvious than this one:
1
|
|
Again note that this syntax also only works in Python 3.
Capturing arbitrary keyword arguments
What if you want to write a function that captures an arbitrary number of keyword arguments?
For example the string format method accepts any keyword argument you give it:
1 2 |
|
How can you write such a function?
Python allows functions to capture any keyword arguments provided to them using the **
operator when defining the function:
1 2 3 4 5 6 |
|
That **
operator will allow our format_attributes
function to accept any number of keyword arguments. The given arguments will be stored in a dictionary called attributes
.
Here’s an example use of our function:
1 2 |
|
Calling functions with arbitrary arguments
Just as you can define functions that take arbitrary keyword arguments, you can also call functions with arbitrary keyword arguments.
By this I mean that you can pass keyword arguments into a function based on items in a dictionary.
Here we’re manually taking every key/value pair from a dictionary and passing them in as keyword arguments:
1 2 3 |
|
This approach of hard-coding the keyword arguments in our function call requires that we know every key in the dictionary we’re using at the time our code is written. This won’t work if we have a dictionary with unknown keys.
We can pass arbitrary keyword arguments to our function using the **
operator to unpack our dictionary items into keyword arguments in our function call:
1 2 3 |
|
This ability to pass arbitrary keyword arguments into functions and to accept arbitrary keyword arguments inside functions (as we did before) is seen frequently when using inheritance:
1 2 3 |
|
Note: We’re also using the *
operator here for the same kind of capturing and unpacking of positional arguments.
Order matters
Since Python 3.6, functions always preserve the order of the keyword arguments passed to them (see PEP 468). This means that when **
is used to capture keyword arguments, the resulting dictionary will have keys in the same order the arguments were passed.
So since Python 3.6, you’ll never see something like this happen:
1 2 |
|
Instead, with Python 3.6+, arguments will always maintain the order they were passed in:
1 2 |
|
Embrace keyword arguments in Python
An arguments position often doesn’t convey as much meaning as its name. So when calling functions, consider naming arguments that you pass in if it might make their meaning clearer.
When defining a new function, stop to think about which arguments should always be specified as keyword arguments when calling your function. Consider using the *
operator to require those arguments be specified as keyword arguments.
And remember that you can accept arbitrary keyword arguments to the functions you define and pass arbitrary keyword arguments to the functions you call by using the **
operator.
Important objects deserve names and you can use keyword arguments to give your objects the names they deserve!
Practice Python every week
If you’d like to level up your Python skills and get practice using Python-specific features, like keyword arguments, sign up for Python Morsels below.