DEV Community

Cover image for All you need to know about Asterisks in Python
Bas Steins
Bas Steins

Posted on • Originally published at bas.codes

All you need to know about Asterisks in Python

Most developers know the asterisk (*) character as multiplication operator in Python:

product = 4 * 2  # 8
Enter fullscreen mode Exit fullscreen mode

However, the asterisk has some special meaning for list or dictionary data structures.

*args and **kwargs

*args in the function definition

The most widely known usage of the asterisk operator, also known as destructuring operator, in Python is its usage in functions.

Let's say you have a function that can add values and returns the sum of these values:

def add(number_1, number_2):
    return number_1 + number_2

print(add(1,2)) # 3
Enter fullscreen mode Exit fullscreen mode

What if you want to sum up an arbitrary number of values? You can add an asterisk in front of the argument's name like so:

def add(*numbers):
    sum = 0
    for number in numbers:
        sum += number
    return sum
Enter fullscreen mode Exit fullscreen mode

As you might have already noticed from the function's body, we expect numbers to be a list. And, indeed, we can now call the function with an arbitrary number of arguments:

add(1, 2, 3, 4) # 10
Enter fullscreen mode Exit fullscreen mode

* in the function call

We have seen so far that we can define a function so that it takes a list of parameters. But what if we have a function with a fixed set of arguments and we want to pass a list of values to it. Consider this function:

def add(number_1, number_2, number_3):
    return number_1 + number_2 + number_3
Enter fullscreen mode Exit fullscreen mode

This function takes exactly 3 parameters. Let's assume we have a list with exactly three elements. Of course, we could call our function like this:

my_list = [1, 2, 3]

add(my_list[0], my_list[1], my_list[2])
Enter fullscreen mode Exit fullscreen mode

Luckily the destructuring operator (*) works in both ways. We have already seen the usage in a function's definition, but we can use it also to call a function:

my_list = [1, 2, 3]

add(*my_list)
Enter fullscreen mode Exit fullscreen mode

**kwargs in function definition

In the same way we can destructure lists with the asterisk (*) operator, we can use the double-asterisk (**) operator to destructure dictionaries in Python functions.

Consider a function like this:

def change_user_details(username, email, phone, date_of_birth, street_address):
    user = get_user(username)
    user.email = email
    user.phone = phone
    ...
Enter fullscreen mode Exit fullscreen mode

If we call it with keyword arguments (kwargs), a function call might look like this:

change_user_details('bascodes', email='blog@bascodes.example.com', phone='...', ...)
Enter fullscreen mode Exit fullscreen mode

In that case we could rewrite our function like this to accept an arbitrary number of keyword arguments that are then represented as a dictionary called kwargs:

def change_user_details(username, **kwargs):
    user = get_user(username)
    user.email = kwargs['email']
    user.phone = kwargs['phone']
    ...
Enter fullscreen mode Exit fullscreen mode

Of course, we can use the kwargs dictionary like any other dictionary in Python, so that the function might become a bit cleaner if we actually utilize the dictionary data structure like so:

def change_user_details(username, **kwargs):
    user = get_user(username)
    for attribute, value in kwargs:
        setattr(user, attribute, value)
    ...
Enter fullscreen mode Exit fullscreen mode

**kwargs in function definition

Of course, the ** operator works for calling a function as well:

details = {
    'email': 'blog@bascodes.example.com',
    ...
}
change_user_detail('bascodes', **details)
Enter fullscreen mode Exit fullscreen mode

Restricting how functions are called

Keyword arguments only

One of the most surprising features of the asterisk in function definitions is that it can be used standalone, i.e. without a variable (parameter) name. That being said, this is a perfectly valid function definition in Python:

def my_function(*, keyword_arg_1):
    ...
Enter fullscreen mode Exit fullscreen mode

But what does the standalone asterisk do in that case? The asterisk catches all (non-keyword) arguments in a list as we have seen above. There is no variable name in our case that would make the list. After the * we have a variable called keyword_arg_1. Since the * has already matched all positional arguments, we're left with keyword_arg_1, which must be used as a keyword argument.

Calling the above function with my_function(1) will raise an error:

TypeError: my_function() takes 0 positional arguments but 1 was given
Enter fullscreen mode Exit fullscreen mode

Positional arguments only

What if we want to force the users of our function to use positional arguments only – as opposed to keyword arguments only in the last example?

Well, there is a very Pythonic way. We interpret the / sign (the opposite of multiplication) to do the trick:

def only_positional_arguments(arg1, arg2, /):
    ...
Enter fullscreen mode Exit fullscreen mode

Surprisingly few Python developers know about this trick which has been introduced to Python 3.8 through PEP 570.

If you call the last function with only_positional_arguments(arg1=1, arg2=2), this will raise a TypeError:

TypeError: only_positional_arguments() got some positional-only arguments passed as keyword arguments: 'arg1, arg2'
Enter fullscreen mode Exit fullscreen mode

Usage of * and ** in literals

The asterisk (*) and double asterisk (**) operators do not only work for function definitions and calls, but they can be used to construct lists and dictionaries.

Constructing lists

Let's say, we have two lists and want to merge them.

my_list_1 = [1, 2, 3]
my_list_2 = [10, 20, 30]
Enter fullscreen mode Exit fullscreen mode

Of course, we could merge these lists with the + operator:

merged_list = my_list_1 + my_list_2
Enter fullscreen mode Exit fullscreen mode

However, the * operator gives us a bit more flexibility. Let's say, we want to include a scalar value in the middle, we could use:

some_value = 42
merged_list = [*my_list_1, some_value, *my_list_2]

# [1, 2, 3, 42, 10, 20, 30]
Enter fullscreen mode Exit fullscreen mode

Constructing dictionaries

Again, what is true for lists and the asterisk (*) operator is true for dictionaries and the double-asterisk operator (**).


social_media_details = {
    'twitter': 'bascodes'
}

contact_details = {
    'email': 'blog@bascodes.example.com'
}

user_dict = {'username': 'bas', **social_media_details, **contact_details}
# {'email': 'blog@bascodes.example.com', 'twitter': 'bascodes', 'username': 'bas'}
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
vulcanwm profile image
Medea

Very interesting!

Collapse
 
bascodes profile image
Bas Steins

Thank you! Glad that it helped you!