(*) and (**) in Python simplified

Ridwan Yusuf
4 min readJan 19, 2023

If you have been programming in Python for a while, then the chances are that you have come across a syntax that looks like this: (*args and **kwargs).

These two syntaxes, (args and **kwargs), are used in both function declaration and function call. It’s important to note that the words ‘args’ and ‘kwargs’ after a single asterisk (*) and double asterisk (**) respectively, are not enforced and can be replaced with any variable name, but ‘args’ and ‘kwargs’ are commonly used in the Python community. ‘args’ is short for ‘arguments’ while ‘kwargs’ stands for ‘keyword arguments’.

Arguments are the data/values passed to a function call.
Parameter are variables listed during function declaration, they serve as dependencies for the function.

[*]:: Single asterisk(*)
1. Whenever you define a function and you put *args as the parameter, what you are simply saying is that as many positional arguments as possible can be passed during the function call. You are not restricting the caller of the function to any specific number of positional arguments. It’s like saying ‘Pass me any number of positional arguments, I know how to handle them’.

Positional arguments:When arguments are passed to a function call in a specific order, they would be captured in the function using the order. That is, the 1st argument maps to the first parameter, the 2nd argument maps to the second parameter and so on.

Let’s take a look at this.

def count_numbers(*args):
print(args)

In ‘count_numbers’ function definition up here, we use *args as the parameter. What that simply means is that when ‘count_numbers’ function would be called, any number of arguments(positional) can be passed to it. So let’s try it out.
[*]Usage

count_numbers(1,2,3,4,5)

[*]Output
(1, 2, 3, 4, 5)
[*]Usage

count_numbers(1,2)

[*]Output
(1, 2)

It becomes obvious that the positional arguments passed during the function call(i.e. counter_numbers(1,2)) are captured as a Tuple(data type) in the function. So we can therefore access them based on there position(index starts from zero)
[*]Snippet

def count_numberss(*args):
a = args[0]
b = args[1]
print("a:",a)
print("b",b)

[*]Usage

count_numbers(1,2)

[*]Output
a: 1
b 2

2. The second usage of * is in function call. Whenever you call/invoke a function, and pass the arguments as (*var). What you are doing is function argument unpacking. Putting * before an iterable will unpack(i.e. separate) it and pass its items as separate positional argument to the function call. A simple example explains better.

[*]Snippet

def printer(a,b,c):
print("A:", a)
print("B:", b)
print("C:", c)

my_list_items = ["A", "B", "C"]
printer(*my_list_items)

We declared a variable called my_list_items, then while calling ‘printer’ function, we passed the argument ‘my_list_items’ prefixed with *. Like we mentioned, the iterable(e.g. my_list_items in this case) would be unpacked and its element would be passed as a separate positional arguments.
So this is what happens after the iterable is unpacked and elements passed separately
printer(“A”, “B”, “C”)

[*]Output
A: A
B: B
C: C

Note that the element that would be unpacked must be an iterable(e.g. strings, tuple, list, etc)

[*]:: Double asterisks(**)
1. Just like how single asterisk(*) deals with positional arguments, ** deals with keyword arguments.
Keyword arguments are identified by the their names rather than their positions.
- Whenever you define a function and you put **kwargs as the parameter, what that means is that the caller of the function can pass different keyword arguments without you restricting them. The cool thing about this is that it gives you the flexibility to pick whatever you need from the arguments. Say you have a function that you only need the username in the function. The idea is that your code will always work even if the caller of the function pass some additional arguments(e.g. age) that you don’t need.

Recall that arguments pass with * (single asterisk) are captured as Tuple as explained above. Arguments passed with **(double asterisks) are captured as a python dictionary
Let’s look at this function
[*]Snippet

def greet(**kwargs):
print("Good day,", kwargs['name'])
print("All keyword args",kwargs)

greet(name="Ray", email="a@gmail.com", is_active=True)

[*]Output
Good day, Ray
All keyword args {‘name’: ‘Ray’, ‘email’: ‘a@gmail.com’, ‘is_active’: True}

Great! **kwargs gives the flexibility to pass as many keywords argument as possible.

2. When you call a function and you pass the arguments as **var, it means that the ‘var’ dictionary should be passed into the function call as keywords argument.
[*]Snippet

var = {"name":"Kay","user_id":9}
greet(**var)

Essentially, what you saying here is that the ‘var’ dictionary(data type) should be passed into greet() function as keywords argument.
So it what you have written is equivalent to:
greet(name=”Kay”, user_id=9)

Time to test
[*]Snippet

def greet(**kwargs):
print("Good day,", kwargs['name'])
print("All keyword args",kwargs)

var = {"name":"Kay","user_id":9}
greet(**var)

[*]Output
Good day, Kay
All keyword args {‘name’: ‘Kay’, ‘user_id’: 9}

It works!!!!

Recap:

  • In function definition, *args means any number of positional arguments can be passed in the function call. It would be captured as tuple
  • In function call, *args means unpacking iterables. each element of the iterables will be passed as separate positional arguments in the function call
  • In function definition, **kwargs means any number of keyword arguments can be passed in the function call. It would be captured as a dictionary
  • In function call, **kwargs means the dictionary should be passed into the function call as keyword arguments.

Thanks for reading and feel free to explore my video collection on YouTube for more educational content. Don’t forget to subscribe to the channel to stay updated with future releases.

--

--

Ridwan Yusuf

RidwanRay.com -I provide engineers with actionable tips and guides for immediate use