Python Topics : args and kwargs
Passing Multiple Arguments to a Function
*args and **kwargs allows passing multiple arguments or keyword arguments to a function
a simple function that takes two arguments and returns their sum
def my_sum(a, b):
    return a + b
the function is limited to only two arguments
Using the Python args Variable in Function Definitions
can pass a list or set of arguments
def my_sum(my_integers):
    result = 0
    for x in my_integers:
        result += x
    return result

list_of_integers = [1, 2, 3]
print(my_sum(list_of_integers))
works but requires creating a list of arguments

*args allows passing a varying number of positional arguments

def my_sum(*integers):
    result = 0
    for x in integers:
        result += x
    return result

print(my_sum(1, 2, 3))
not passing a list to my_sum function
instead passing three different positional arguments
my_sum() takes all the parameters that are provided in the input and packs them all into a single iterable object named args
the object is a tuple which is immutable
function uses the unpacking operator (*)
args is just a name, any name can be used
Using the Python kwargs Variable in Function Definitions
**kwargs works just like *args
instead of accepting positional arguments it accepts keyword/named arguments
def concatenate(**kwargs):
    result = ""
    # Iterating over the Python kwargs dictionary
    for arg in kwargs.values():
        result += arg
    return result

print(concatenate(a="Real", b="Python", c="Is", d="Great", e="!"))
the argument is a dict
function uses the unpacking operator (**)
kwargs is just a name, any name can be used
Ordering Arguments in a Function
create a function that takes a changeable number of both positional and named arguments
argument order counts
  1. standard arguments
  2. *args arguments
  3. **kwargs arguments
def my_function(a, b, *args, **kwargs):
    pass
Unpacking With the Asterisk Operators: * & **
>>> my_list = [1, 2, 3]
>>> print(my_list)
[1, 2, 3]
# unpacking the list argument
>>> print(*my_list)
1, 2, 3
when using the * operator to unpack a list and pass arguments to a function, it's exactly as though passing every single argument alone
def my_sum(*args):
    result = 0
    for x in args:
        result += x
    return result

list1 = [1, 2, 3]
list2 = [4, 5]
list3 = [6, 7, 8, 9]

print(my_sum(*list1, *list2, *list3))
can merge two different dictionaries by using the unpacking operator **
my_first_dict = {"A": 1, "B": 2}
my_second_dict = {"C": 3, "D": 4}
my_merged_dict = {**my_first_dict, **my_second_dict}

print(my_merged_dict)
index