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.
In function construction In function call
=======================================================================
| def f(*args): | def f(a, b):
*args | for arg in args: | return a + b
| print(arg) | args = (1, 2)
| f(1, 2) | f(*args)
----------|--------------------------------|---------------------------
| def f(a, b): | def f(a, b):
**kwargs | return a + b | return a + b
| def g(**kwargs): | kwargs = dict(a=1, b=2)
| return f(**kwargs) | f(**kwargs)
| g(a=1, b=2) |
-----------------------------------------------------------------------