  ## Understanding *args

In Python, the single-asterisk form of args can be used as a parameter to send a non-keyworded variable-length argument list to functions. It is worth noting that the asterisk () is the critical element here, as the word args is the established conventional idiom, though the language does not enforce it.

Suppose we create a function to add two integer numbers like this.

```def calculate_sum(a, b):
return a + b```

In the code above, we create the function with a and b as arguments, and then when we call the function, we pass two numbers to each argument…

In this case, we will pass number `5` in for `a` and the integer `6` in for `b`:

Output

```if __name__ == '__main__':
result = calculate_sum(a=5, b=6)
print(f"The sum is {result}")

#output: The sum is 11```

What is happen if, later on, we decide that we would like to sum the total of three numbers rather than just two? If we try to add a number to the function, as shown below, we’ll receive an error.

```if __name__ == '__main__':
result = calculate_sum(a=5, b=6 , c=8)
print(f"The sum is {result}")
#output : TypeError: calculate_sum() takes 2 positional arguments but 3 were given```

So, if you suspect that you may need to use more arguments, later on, you can make use of *args as your parameter instead.
We can essentially create the same function and code that we showed in the first example by removing x and y as function parameters and instead of replacing them with *args as the example below.

```def calculate_sum(*args):
x = 0
for num in args:
x += num
return x```

Output

```if __name__ == '__main__':
result_1 = calculate_sum(5, 5, 5)
result_2 = calculate_sum(5, 5, 5, 5)
result_3 = calculate_sum(5, 5, 5, 5, 5)
result_4 = calculate_sum(5, 5, 5, 5, 5, 5)

print(f"The sum is {result_1}")
#output : The sum is 15
print(f"The sum is {result_2}")
#output : The sum is 20
print(f"The sum is {result_3}")
#output : The sum is 25
print(f"The sum is {result_4}")
#output : The sum is 30```

Because we used *args to pass a variable argument list to our function, we could pass in as many arguments as we want into the function calls.

With `*args` we can create more flexible code that accepts a varied amount of non-keyworded arguments within your function.

## Understanding **kwargs

In the function below, there are four parameters defined. That is `name`, surname,  `score`, and `is_active`. The function will print out each of these arguments.

```def preset_args(name: str, surname: str, score: float, is_active: bool):
print(f"The value of argument name is : {name}")
print(f"The value of argument surname is : {surname}")
print(f"The value of argument score is : {score}")
print(f"The value of argument is_active is : {is_active}")```

When we run the program with the `python preset_args.py` command, we’ll receive the following output:

```if __name__ == '__main__':
preset_args(name="Christos", surname="Ploutarchou", score=15, is_active=True)
#output : The value of argument name is : Christos
#output : The value of argument surname is : Ploutarchou
#output : The value of argument score is : 15
#output : The value of argument is_active is : True```

The double asterisk in kwargs (**kwargs) is used to pass a keyworded variable argument to a function.

Like *args, **kwargs can take however many arguments you would like to supply to it. However, **kwargs differs from *args in that you will need to assign keywords to each value.

First, let’s print out the **kwargs arguments that we pass to a function.

```def print_kwargs(**kwargs):
for key, value in kwargs.items():
print(f"The key: {key}, The value: {value}")```

Let’s run the above function using two different ways and look at the output:

Example One (Pass argument in function)

```def print_kwargs(**kwargs):
for value in kwargs:
print(value)```

### Now let’s run the function and set argument as default parameters of the function

```if __name__ == '__main__':
print_kwargs("Christos", "Ploutarchou", 15, True)```

Output

```Traceback (most recent call last):
File "/home/christos/PycharmProjects/Tutorials/main.py", line 41, in <module>
print_kwargs("Christos", "Ploutarchou", 15, True)
TypeError: print_kwargs() takes 0 positional arguments but 4 were given```

By running function print_kwargs and pass only value, we get an exception. That happens because we use double asterisk notation (**), then it is required to pass key for each value we give in the function.

Example 2 (Pass keyworded variable arguments)

```if __name__ == '__main__':
print_kwargs(name="Christos", surname="Ploutarchou", score=15, is_active=True)

#output: The value of the argument name is: Christos
#output: The value of argument surname is: Ploutarchou
#output: The value of the argument score is: 15
#output: The value of argument is_active is: True```

The function successfully executed without any exception by running function print_kwargs and passing the arguments by set key for each value.

## Conclusion

We can use the particular syntax of *args and **kwargs within a function definition to pass a variable number of arguments to the function.

Creating functions that accept *args and **kwargs are best used in situations where you expect that the number of inputs within the argument list will remain relatively small. The use of *args and **kwargs is primarily to provide readability and convenience but should be done with care.