How to? : *args and **kwargs in Python
Tags: python, tutorial, beginners, todayilearned
Published: 2020-12-26 16:03:52

Python offers several ways to pass on arguments to a function body. 
Suppose we are making a function that returns the area of a rectangle. We can write the basic code like this:
![image](https://user-images.githubusercontent.com/13352807/101327234-6bcf6000-3894-11eb-8757-749a1ffcc788.png)

This given function will calculate area of a given rectangle given its height and width. 

Now, if we want to make this function generic to find hyper volume of a cuboid given its n dimensions, we have two ways to move forward:
1. Change the function to take n dimensions as arguments: `calculate_volume(x1, x2, x3 ... xn)`, this seems really messy and wont even work on n-1 dimensions. 
2. Change the function to take a list of n dimensions as a single argument:`calculate_volume([x1, x2, x3, ... xn])`, this seems much better, although we will have to pass on a list of dimensions which isn't really that nice. 

## Understanding *args:
The important part here is the `*`, and not the `args`. We can use any variable name according to the function use case.
Now let's use this to solve our current problem:

![image](https://user-images.githubusercontent.com/13352807/101330500-af2bcd80-3898-11eb-8a8c-05855d21031e.png)

As the `dimensions` argument can take any number of arguments, we can now pass several dimensions in it to return the volume. 

We can further improve this logically by using both fixed and extended arguments :
![image](https://user-images.githubusercontent.com/13352807/101333935-3aa75d80-389d-11eb-8e7b-5d5fdd5c52c4.png)

Now, as a valid shape will be having at least one dimension, so we can pass it as `first_dimension` and all other dimensions in `extra_dimensions`. 

## Understanding **kwargs
This is used for variable length keyword arguments. Use case is a function that where we dont know the arguments or we need to pass on all the arguments to some other function. 
It is basically a dictionary that can hold all the keyword arguments together and we can simply iterate over its key-value pair to access them. 
Example:
Here I'm making an automatic html generator which generates the tag given all its attributes. We dont need to worry about handling all the keyword arguments, we simply pass them in **tags.

![image](https://user-images.githubusercontent.com/13352807/103154854-9be98f00-47c0-11eb-813a-d0b9fa15f102.png)

Output: `'< img src = "mountain.jpg" width = "500" height = "600" >'`












## Combining everything:
One real life use for these are in arguments forwarding.
Imagine we are working on a big project with several functions, and now we want to monitor the time taken for each function in the code. 
Basic way would be to go to function definition and encapsulate it with in `start_time` and `end_time`, then logging out the difference as time taken. This will require heavy code change and is not advisable at all. 

Another way would be to make a generic trace function that will take any other function as argument and returns the time taken by it. 

![image](https://user-images.githubusercontent.com/13352807/101368040-aead2a00-38cc-11eb-9ad2-0e8229505577.png)

Since the `trace` function is generic in nature, it doesn't care about the arguments of `f`. We can simply pass them as args and kwargs without changing the function definition. 

For example, here we have a function that calculates the exponent value given 2 numbers (which of course has been modified for this example)
![image](https://user-images.githubusercontent.com/13352807/101369202-05ffca00-38ce-11eb-8bcf-da6ba31c633f.png)

Although we can do this in a much elegant way, that I'll continue in the next post.