All you need to know about Asterisks in Python
Tags: python, beginners
Published: 2022-05-25 19:12:03


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

```python
product = 4 * 2  # 8
```

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:

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

print(add(1,2)) # 3
```

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:

```python
def add(*numbers):
    sum = 0
    for number in numbers:
        sum += number
    return sum
```

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:

```python
add(1, 2, 3, 4) # 10
```

### `*` 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:

```python
def add(number_1, number_2, number_3):
    return number_1 + number_2 + number_3
```

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:

```python
my_list = [1, 2, 3]

add(my_list[0], my_list[1], my_list[2])
```

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:

```python
my_list = [1, 2, 3]

add(*my_list)
```

### `**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:

```python
def change_user_details(username, email, phone, date_of_birth, street_address):
    user = get_user(username)
    user.email = email
    user.phone = phone
    ...
```

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

```python
change_user_details('bascodes', email='blog@bascodes.example.com', phone='...', ...)
```

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`:

```python
def change_user_details(username, **kwargs):
    user = get_user(username)
    user.email = kwargs['email']
    user.phone = kwargs['phone']
    ...
```

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:

```python
def change_user_details(username, **kwargs):
    user = get_user(username)
    for attribute, value in kwargs:
        setattr(user, attribute, value)
    ...
```

### `**kwargs` in function definition

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

```python
details = {
    'email': 'blog@bascodes.example.com',
    ...
}
change_user_detail('bascodes', **details)
```

## 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:

```python
def my_function(*, keyword_arg_1):
    ...
```

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
```


### 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:

```python
def only_positional_arguments(arg1, arg2, /):
    ...
```

Surprisingly few Python developers know about this trick which has been introduced to Python 3.8 through [PEP 570](https://peps.python.org/pep-0570/).

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'
```

## 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.

```python
my_list_1 = [1, 2, 3]
my_list_2 = [10, 20, 30]
```

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

```python
merged_list = my_list_1 + my_list_2
```

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:


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

# [1, 2, 3, 42, 10, 20, 30]
```


### Constructing dictionaries

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

```python

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'}
```