Sign in to your Python Morsels account to save your screencast settings.
Don't have an account yet? Sign up here.
In Python, you can pass arbitrary keyword arguments to functions.
You may have seen an asterisk (*) used to unpack an iterable into separate positional arguments in a function call:
>>> numbers = [2, 1, 3, 4, 7, 11, 18]
>>> print(*numbers)
2 1 3 4 7 11 18
You may have also seen an asterisk used in a function definition to make a function that accepts any number of positional arguments:
>>> from random import randint
>>> def roll(*dice):
... return sum(randint(1, die) for die in dice)
...
>>> roll(6, 6)
7
What if you wanted to do the same thing but with keyword arguments?
Note: If you're not sure what keyword arguments are, see positional and keyword arguments.
When defining a function, you can use double asterisk (**) to capture all keyword arguments that are provided to the function when it's called:
def print_properties(**properties):
for name, value in properties.items():
print(f"{name}: {value}")
We can call that function with any keyword argument that we can think up:
>>> print_properties(color="purple", size="L", price=5)
color: purple
size: L
price: 5
We just called that function with the keyword arguments color, size, and price, but any argument names would be accepted.
Just as * has one use in function definitions and one use in function calls, ** can also be used in both function definitions and in function calls.
When calling a function, you can use ** to unpack a dictionary of key-value pairs into separate keyword arguments.
Here we're passing the sep and end arguments to this print call:
>>> kwargs = {"sep": ", ", "end": "!\n"}
>>> numbers = [2, 1, 3, 4, 7, 11]
>>> print(*numbers, **kwargs)
2, 1, 3, 4, 7, 11!
That's the same as this code, which hard-codes those keyword arguments:
>>> numbers = [2, 1, 3, 4, 7, 11]
>>> print(*numbers, sep=", ", end="!\n")
2, 1, 3, 4, 7, 11!
It's usually better to hardcode keyword arguments, because code is usually more readable this way.
But there are times when dynamic keyword arguments can be very handy.
Using ** can be handy with functions that accept any keyword arguments, like the print_properties function that we made before:
>>> values = {"color": "purple", "size": "L"}
>>> print_properties(**values)
color: purple
size: L
Note that when using **, the keys in the given dictionary must be strings, and they'll typically be strings that represent valid Python variable names.
** with the string format methodThese two uses of **, one for function calling and one for function definitions, often go together.
For example, you'll see ** unpacking used with functions that accept arbitrary keyword arguments, like the string format method.
The format method accepts any keyword arguments that we can provide, and it matches the names of those arguments to any named replacement groups within the template string that we're operating on:
>>> base_url = "https://api.stackexchange.com/2.3/questions/{ids}?site={site}"
>>> context = {"ids": "33809864;2759323;9321955", "site": "stackoverflow"}
>>> url_for_questions = base_url.format(**context)
>>> url_for_questions
'https://api.stackexchange.com/2.3/questions/33809864;2759323;9321955?site=stackoverflow'
So ** keyword argument unpacking is often used with functions like the string format method that accept arbitrary keyword arguments.
** with super for easier class inheritanceBut the most common use for ** is within class inheritance.
Here we have a class that inherits from another class:
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user and user.is_authenticated:
self.fields["email"].initial = user.email
Note that both * and ** are being used in this method to capture all positional and keyword arguments that are provided in each call to this initializer method.
Those captured arguments are then unpacked into positional and keyword arguments provided to the parent class's method with the same name:
class ContactForm(forms.Form):
...
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
...
Using * and ** for class inheritance allows us to capture all the arguments given to our method and pass them up to our parent class.
This is a way for our method to delegate the responsibility of accepting those arguments to our parent.
** for keyword argument packing and unpacking in PythonThose are the two uses of ** in Python:
** allows that function to accept any keyword argument that might be given to it** is for unpacking a dictionary of key-value pairs into the keyword arguments that are given to that functionThe most common use of ** is in class inheritance.
We don't learn by reading or watching. We learn by doing. That means writing Python code.
Practice this topic by working on these related Python exercises.
Python, like many programming languages, has functions. A function is a block of code you can call to run that code.
Python's functions have a lot of "wait I didn't know that" features. Functions can define default argument values, functions can be called with keyword arguments, and functions can be written to accept any number of arguments.
To track your progress on this Python Morsels topic trail, sign in or sign up.
Sign in to your Python Morsels account to track your progress.
Don't have an account yet? Sign up here.