Tutorials
python

Usage of Asterisks in Python

Many Python users are familiar with using asterisks for multiplication and power operators, but in this tutorial, you'll find out additional ways on how to apply the asterisk.

Most of us use asterisks as multiplication and power operators, but they also have a wide range of operations being used as a prefix operator in Python. After reading this article, you will get to know the full usage of asterisks.

Asterisks have many particular use cases in Python. In general, we are familiar with the multiplication and power operators. It can perform some other operations like unpacking, arguments passing, etc.., in different situations. First, let's see the general usage of asterisks.

General Usage Of * and **

* is used as multiplication operator whereas ** is used as a power operator.

## declaring variables
a, b = 2,3

## multiplication
print("Multiplication of 2 and 3 is {}".format(a * b))

## as power operator
print("2 power 3 is {}".format(a ** b))
Multiplication of 2 and 3 is 6
2 power 3 is 8

Now you have seen the general and most commonly used asterisks. There are however asterisks use cases which you may not know. Let's move to extract the hidden usage of asterisks.

Learning Concepts

  1. Unpacking Using *

    1.1. Combining Different Iterables

    1.2. Netsted Unpacking

 2. Unpacking Using **

 3. Asterisks In Functions

    3.1. Unpacking In Functions

    3.2. Packing In Functions

 4. Keyword-Only Arguments With Positional Arguments

 5. Keyword-Only Arguments Without Positional Arguments

1. Unpacking Using *

Unpacking the elements from a list/tuple/set using the *. We are going to use unpacking in future code to implement some other features of asterisks. Unpacking is the basic idea behind using the asterisks as prefix operators.

Let's assume that we want to assign the first element of list/tuple/set to a and remaining elements to b. In general, we will accomplish this using slicing.

## general approach
nums = [i for i in range(1, 6)]
a = nums[0]
b = nums[1:]
print(a, b)

print("--------------------------")

## hard approach
squares = [i ** 2 for i in range(1, 6)]
a = squares[0]
b = []
for i in range(1, len(squares)):
    b.append(squares[i])
print(a, b)
1 [2, 3, 4, 5]
--------------------------
1 [4, 9, 16, 25]

Now, we are going to use the * operator to implement this in a single line of code.

nums = [i for i in range(1, 6)]

## a will be 1 and b will be a list containing remaining elements
a, *b = nums
print(a, b)
1 [2, 3, 4, 5]

Here a is a type of variable that it gets from the list and b is a list. Unpacking can work on tuples and sets the same as it does with the lists. Let's see some examples of it.

## using tuple
## here first element will assign to a and last element to c
a, *b, c = (1, 2, 3, 4, 5)
print(a, b, c)

print("--------------------------")

## using sets
a, *b, c = {1, 4, 9, 16, 25}
print(a, b, c)
1 [2, 3, 4] 5
--------------------------
1 [4, 9, 16] 25

1.1. Combining Different Iterables

We can add two different iterables into list/sets/tuples using unpacking. In general, we would have to convert the iterables and combine them into one. It's not necessary though if we use the *. Let's see an example.

## combining a tuple, list, set
nums = [1, 2, 3]
nums2 = (4, 5, 6)
nums3 = {7, 8, 9}

## we convert the combined elements into any iterable we want
## here i am converting into a list
_list = [*nums, *nums2, *nums3]
_tuple = (*nums, *nums2, *nums3)
_set = {*nums, *nums2, *nums3}

print(type(_list))
print(_list)
print("------------------------")
print(type(_tuple))
print(_tuple)
print("------------------------")
print(type(_set))
print(_set)
<class 'list'>
[1, 2, 3, 4, 5, 6, 8, 9, 7]
------------------------
<class 'tuple'>
(1, 2, 3, 4, 5, 6, 8, 9, 7)
------------------------
<class 'set'>
{1, 2, 3, 4, 5, 6, 7, 8, 9}

1.2. Nested Unpacking

Nested unpacking is taking out the elements from the first level of extracted items. See the example to get an idea of what I am talking about.

languages = ["Python", "HTML", "CSS", "JS"]

## unpacking
[[first_letter, *remaining], *other] = languages

print(first_letter, remaining, other)
P ['y', 't', 'h', 'o', 'n'] ['HTML', 'CSS', 'JS']

2. Unpacking Using **

As we saw earlier, ** is used to unpack the elements from a dictionary. We must know the keys of the dictionary to extract the values during unpacking. Let's see an example.

## sample dictionary
person = {"name":"John", "age":19, "year_of_passing":"2021"}
string = "Name:-{name} Year Of Graduation:-{year_of_passing} Age:-{age}".format(**person)
print(string)
Name:-John Year Of Graduation:-2021 Age:-19

It's not common to use the ** to extract the elements from a dictionary.

3. Asterisks In Functions

We can pass and capture the arguments using asterisks in functions to reduce the lines of code.

3.1. Unpacking In Functions

The * operator is used to call a function by unpacking an iterable. Let's say we have a list and have to pass all the elements of it to the print() separately. What will you do? I think you are thinking of a loop. But, * makes this very easy for us.

Just pass the iterable as *iterable_name. It unpacks the iterable and passes all the elements separately.

nums = [i for i in range(1, 6)]
## passsing list using the *
print(*nums)
1 2 3 4 5
nums = (i for i in range(1, 6))
## passsing tuple using the *
print(*nums, sep = ", ")
1, 2, 3, 4, 5

3.2. Packing Elements

Packing means capturing multiple elements at once. We use this feature in function calls.

def average(*nums):
    return sum(nums) / len(nums)

Now, we can pass any number of integers to calculate the average. The function average captures all the arguments as a list.

## calling the average with some numbers
print(average(1, 2, 3, 4, 5))
3.0

We have seen how to capture the multiple elements into a list using *. Now, we will learn how to capture various key-value pair elements using **.

def _object(name, **properties):
    print(name, properties)

_object("Car", color="Red", cost=999999, company="Ferrari")
Car {'ceo': 'Louis', 'color': 'Red', 'cost': 999999, 'company': 'Ferrari'}

4. Keyword-Only Arguments With Positional Arguments

Arguments with the name are called keyword arguments. And the other arguments are called positional arguments. First, you have to understand the difference between keyword and positional arguments. Let's see an example of it.

## name is a positional argument
## color and cost are keyword arguments
def sample(car, color = None, cost = None):
    print("Car:-{} Color:-{}, Cost:-{}".format(car, color, cost))

We can use the name of the parameter to pass the arguments while calling a function for keyword arguments. If we don't give anything for the keyword arguments, they will take the default value provided in the function definition.

We can specify the keyword arguments using the keywords or position of the arguments.

sample("Ferrari", "Red", 999999)
Car:-Ferrari Color:-Red, Cost:-999999
sample("Ferrari", cost = 999999, color = "Red")
Car:-Ferrari Color:-Red, Cost:-999999

The order of keyword arguments is not essential if we call them using the names defined in the function.

sample("Ferrari", color = "Green")
Car:-Ferrari Color:-Green, Cost:-None

Now, you have an idea about positional and keyword arguments. Let's enter into our main topic.

Keyword-Only arguments only are specified by the keywords. We can't set them positionally as we have seen earlier. We have to declare the keyword arguments after the * arguments to capture keyword-only.

def keyword_only(*items, _list, default = False):
    print(items)
    print(_list)
    print(default)

We have to use the _list and default keywords while calling the function as they come after the *items. If we don't use the keywords to call them, we will get an error. Let's see both cases.

nums = [i ** 2 for i in range(1, 6)]
## calling the function
keyword_only(1, 2, 3, 4, 5, _list = nums, default = True)
(1, 2, 3, 4, 5)
[1, 4, 9, 16, 25]
True

What if we call the function keyword_only using the positional arguments.

nums = [i ** 2 for i in range(1, 6)]
## calling the function will raise an error
keyword_only(1, 2, 3, 4, 5, nums, False)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-85-bf737d8a8bfc> in <module>()
      1 nums = [i ** 2 for i in range(1, 6)]
----> 2 keyword_only(1, 2, 3, 4, 5, nums, False)


TypeError: keyword_only() missing 1 required keyword-only argument: '_list'

We have an error stating that we are missing one required keyword-only argument. So, we have to use the keywords while calling the keyword-only arguments. Keyword-Only arguments are defined only after the * arguments in functions.

5. Keyword-Only Arguments Without Positional Arguments

If we want to accept only Keyword-Only arguments without any positional arguments, Python allows us to use * in function parameters to achieve this. Let's see an example.

def _sample(*, name):
    print(name)

The above function takes only the keyword-only arguments. We have to pass the argument with the keyword. Otherwise, we will get an error. * in the function definition is to make sure that the function only takes arguments using the keywords. Any parameters we define after the * parameter in the function must specify with the keywords during the function call.

## calling the function using keyword 'name'
_sample(name = "Datacamp")
Datacamp
## calling the function without using keyword 'name'
## we will get a TypeError
_sample("Datacamp")
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-12-fe0d3718b56c> in <module>()
      1 ## calling the function without using keyword 'name'
      2 ## we will get a TypeError
----> 3 samp("Datacamp")


TypeError: samp() takes 0 positional arguments but 1 was given

It's clear that the _sample() function only takes the keyword-only* arguments.

Conclusion

Developers are adding new features in every new release of Python. Asterisks are special and reduce some parts of the code. We can't accept the multiple positional arguments without *, and keyword-only arguments also need * to implement that feature. Other operations of asterisks may be achieved with multiple lines of code. Practice them to understand it fully. Thanks for reading!

If you would like to learn more about Python, take DataCamp's Python Data Science Toolbox (Part 1) course.

Happy Coding :)

Want to leave a comment?