Optimize code in python using *args and **kwargs

Sandhya Krishnan
5 min readApr 1, 2023
Photo by Antonio Batinić: https://www.pexels.com/photo/black-screen-with-code-4164418/

Python offers the flexibility of passing a variable number of arguments to a function using *argsand **kwargs.

*args

The *args parameter is used when passing a variable number of non-keyworded arguments to a function. Inside the function, *argsis accessed as a tuple, which can be looped over or indexed like a list.

While tuples are similar to lists, they are immutable ordered collections of elements. In other words, once defined, their values cannot be changed. Tuples are defined using parentheses, with each element separated by a comma.

Suppose we have a function named my_arg_function() that accepts *args as arguments. We can determine the type of args using the built-in function, type().

def my_arg_function(*args):
print("Type of args is :", type(args))

my_arg_function(1, 2, 3)
Type of args is : <class 'tuple'>

As *args enables passing an arbitrary number of arguments to a function as a tuple. With this flexibility, functions can be designed to accommodate different input sizes and types, making them more versatile and reusable.

Suppose we have a use case to calculate the product of numbers. We can define the function that accepts *args and multiplies all the numbers together.

def my_arg_function(*args):
pdt = 1
for arg in args:
pdt *= arg

print(f"product of {args} is : {pdt}")

To use this my_arg_function() and calculate the product of 3 or 4 numbers, we can call the function with the required arguments, as below.

my_arg_function(1, 2, 3)
my_arg_function(1, 2, 3, 4)
product of (1, 2, 3) is : 6
product of (1, 2, 3, 4) is : 24

In the first example, we passed 3 arguments and in the second example, we passed 4 arguments, to the same function. With the use of *args, we can call my_arg_function() with any number of arguments, and the function will handle it correctly. This makes the function more flexible and reusable and allows us to calculate the product of any number of numbers without having to change the function every time.

We can also use*args to pass different types of inputs to a function. Suppose, we define a function, diff_input_types() that takes a variable number of arguments using *args, and then iterates over the arguments using a for loop to check the type of input.

def diff_input_types(*args):
for arg in args:
if isinstance(arg, str):
print(f"Input string: {arg}")
elif isinstance(arg, int):
print(f"Input integer: {arg}")
elif isinstance(arg, float):
print(f"Input float: {arg}")
else:
print(f"Unknown input type: {arg}")
diff_input_types("Hello Here!!!", 27, 3.14, True, None)
Input string: Hello Here!!!
Input integer: 27
Input float: 3.14
Input integer: True
Unknown input type: None

In machine learning, we can use *args parameters to define the train_model() function with multiple learning_rate arguments. This allows us to include any number of learning rates as variable-length arguments when calling the function.

Assuming we have the MyModel and MyData classes to represent the machine learning model and training data, respectively, where MyModel has a train() method that takes a learning_rate parameter, and MyData is a simple class used as a placeholder for training data. The train_model() function can be defined to accept a model, data, and a variable number of learning_rates.

Within the function, we iterate over the learning_rates argument and call model.train() for each learning_rate value. Here is an example with 3 training values.

#Note this is only a code snippet
class MyModel:
def __init__(self):
self.weights = None

def train(self, data, learning_rate):
# training logic here
print(f”Training with learning rate {learning_rate}”)

class MyData:
pass

def train_model(model, data, *learning_rates):
for lr in learning_rates:
model.train(data, learning_rate=lr)

model = MyModel()
data = MyData()
train_model(model, data, 0.001, 0.01, 0.1)

**kwargs

**kwargsis used to pass a variable number of keyworded arguments to a function as key-value pairs to a function, which can then be accessed as a dictionary inside the function. The type of kwargs can be determined by using the built-in function, type().

def my_kwargs_function(**kwargs):
print("Type of kwargs is :", type(kwargs))


my_kwargs_function(a=1, b=2, c=3)
Type of kwargs is : <class 'dict'>

We can use **kwargs to write a function that calculates the average of a variable number of values and rounds the result to 2 decimal places, as shown below:

def calculate_average(**kwargs):
print(f"Average of values of {kwargs} is", "{:.2f}".format(sum(kwargs.values()) / len(kwargs)))
calculate_average(a=10, b=20, c=30)
calculate_average(a=1.5, b=2.5, c=3.5, d=4.5)
Average of values of {‘a’: 10, ‘b’: 20, ‘c’: 30} is 20.00
Average of values of {‘a’: 1.5, ‘b’: 2.5, ‘c’: 3.5, ‘d’: 4.5} is 3.00

This function is flexible and can be used to calculate the average of any set of values, making it useful for a variety of data analysis tasks, such as finding the average score on a test or the average age of a group of people.

To clean and validate data using **kwargs we can define an allowed_keys list that specifies the permitted keys. For example, if we want cleaned data to include only name, age, is_student, and occupation, we can define the to_clean_data() function as follows: Thename should be in title case, age should be an integer, is_student should be a boolean and occupation should be capitalized.

def to_clean_data(**kwargs):
allowed_keys = ['name', 'age', 'is_student', 'occupation']
cleaned_data = {}
for key, value in kwargs.items():

if key not in allowed_keys:
raise ValueError(f"{key} is not a valid key")

if key == 'name':
cleaned_value = value.strip().title()
elif key == 'age':
cleaned_value = int(value)
elif key == 'is_student':
cleaned_value = bool(value)
elif key == 'occupation':
cleaned_value = value.strip().capitalize()

cleaned_data[key] = cleaned_value

return cleaned_data
print("cleaned_data_dict :", to_clean_data(name=" Sani mol  ", age="25", is_student=" True "))
print("cleaned_data_dict :", to_clean_data(name="John mATTew ", age="30", is_student="false", occupation="data proFessional "))
cleaned_data_dict : {'name': 'Sani Mol', 'age': 25, 'is_student': True}
cleaned_data_dict : {'name': 'John Mattew', 'age': 30, 'is_student': True, 'occupation': 'Data professional'}

By using **kwargs, the function can accept any number of keyword arguments, making it more flexible, and this function is useful for cleaning and validating data. This could be used in a variety of contexts, such as data processing, web development, or data analysis.

While using *args and **kwargs, it’s important to remember to include the asterisks () before args and kwargs, to avoid a syntax error. When unpacking a dictionary using **kwargs, the keys in the dictionary should match the parameter names in the function definition. If a key in the dictionary does not match any parameter name, it will lead to a TypeError. Similarly, if a required parameter is missing from the dictionary, it will also result in a TypeError. It’s important to ensure that the keys in the dictionary and the parameter names in the function definition are consistent and match each other.

--

--