Passing a list as an argument and prefixing it with a single asterisk (*) will cause each item of the list, in order, to be used as a separate positional argument. Passing a dictionary and prefixing it with two asterisks (**) will cause the keys of the dictionary to be used as names for separate keyword arguments and the dictionary’s values to become the values of these arguments.
When a Python function needs to accept arbitrary sets of optional arguments, or to accept many different arguments based on different situations, it’s common to define it like this:
def my_func(*args, **kwargs):
The function will then have access to a list named args containing all the positional arguments passed to it and a dictionary named kwargs containing all the keyword arguments passed to it. The function can then look at those variables to work out what it needs to do.
Practical Django Projects, 2nd edition, James Bennet, Apress.