Python Study 1 - * and **

* vs **

They allow for functions to be defined to accept and for users to pass any number of arguments, positional (*) and keyword (**).

*args vs **kwargs when defining functions

*args allows for any number of optional positional arguments (parameters), which will be assigned to a tuple named args.
**kwargs allows for any number of optional keyword arguments (parameters), which will be in a dict named kwargs.

Positional Arguments

1
2
3
4
5
6
7
8
9
10
def test(a,b,c):
print(a)
print(b)
print(c)

test(1,2,3)
#output:
1
2
3

This is a function definition with positional arguments. You can call it with keyword/named arguments as well:

1
2
3
4
5
6
7
8
9
10
def test(a,b,c):
print(a)
print(b)
print(c)

test(b=1,a=2,c=3)
#output:
2
1
3

Keyword Arguments

1
2
3
4
5
6
7
8
9
10
11
12
def test(a=0,b=0,c=0):
print(a)
print(b)
print(c)
print('-------------------------')

test(a=1,b=2,c=3)
#output :
1
2
3
-------------------------

Can call this function with positional arguments as well:

1
2
3
4
5
6
7
8
9
10
11
12
def test(a=0,b=0,c=0):
print(a)
print(b)
print(c)
print('-------------------------')

test(1,2,3)
# output :
1
2
3
---------------------------------

* vs **

These operators can be used in 2 areas:
a) function call
b) function definition

The use of * operator and ** operator in function call
1
2
3
4
5
6
7
8
9
10
11
12
13
def sum(a,b):   #receive args from function calls as sum(1,2) or sum(a=1,b=2)
print(a+b)

my_tuple = (1,2)
my_list = [1,2]
my_dict = {'a':1,'b':2}

# Let us unpack data structure of list or tuple or dict into arguments with help of '*' operator
sum(*my_tuple) # becomes same as sum(1,2) after unpacking my_tuple with '*'
sum(*my_list) # becomes same as sum(1,2) after unpacking my_list with '*'
sum(**my_dict) # becomes same as sum(a=1,b=2) after unpacking by '**'

# output is 3 in all three calls to sum function.

Conclusion: when the * or ** operator is used in a function call

* operator unpacks data structure such as a list or tuple into arguments needed by function definition.

** operator unpacks a dictionary into arguments needed by function definition.

The use of * operator in function definition

1
2
3
4
5
6
7
8
9
def sum(*args): #pack the received positional args into data structure of tuple. after applying '*' - def sum((1,2,3,4))
sum = 0
for a in args:
sum+=a
print(sum)

sum(1,2,3,4) #positional args sent to function sum
#output:
10

In function definition the * operator packs the received arguments into a tuple.

The use of ** operator in function definition

1
2
3
4
5
6
7
def sum(**args): #pack keyword args into datastructure of dict after applying '**' - def sum({a:1,b:2,c:3,d:4})
sum=0
for k,v in args.items():
sum+=v
print(sum)

sum(a=1,b=2,c=3,d=4) #positional args sent to function sum

In function definition The ** operator packs the received arguments into a dictionary.

Therefore:

In a function call the * unpacks data structure of tuple or list into positional or keyword arguments to be received by function definition.

In a function call the ** unpacks data structure of dictionary into positional or keyword arguments to be received by function definition.

In a function definition the * packs positional arguments into a tuple.

In a function definition the ** packs keyword arguments into a dictionary.