When creating functions, particularly complex ones meant for reuse in data analysis or machine learning workflows, situations arise where the exact number of arguments a user might pass is unknown beforehand. Python offers powerful mechanisms to manage this flexibility: *args for variable non-keyword arguments and **kwargs for variable keyword arguments.
*argsSometimes, you want a function to accept any number of positional arguments. For instance, imagine a function designed to calculate the average of several measurements, but the number of measurements might vary each time it's called. This is where *args comes in handy.
When you define a function parameter preceded by an asterisk (*), like *args, Python collects any extra positional arguments passed during the function call into a tuple assigned to that parameter. The name args is a convention, but you could technically use any valid variable name (e.g., *numbers, *values).
Consider this example:
def calculate_sum(*numbers):
"""Calculates the sum of an arbitrary number of numeric arguments."""
print(f"Received arguments as a tuple: {numbers}")
total = 0
for number in numbers:
total += number
return total
# Calling the function with different numbers of arguments
sum1 = calculate_sum(1, 2, 3)
print(f"Sum 1: {sum1}")
sum2 = calculate_sum(10, 20, 30, 40, 50)
print(f"Sum 2: {sum2}")
sum3 = calculate_sum(5)
print(f"Sum 3: {sum3}")
sum4 = calculate_sum() # Works even with no extra arguments
print(f"Sum 4: {sum4}")
In each call, the arguments 1, 2, 3 or 10, 20, 30, 40, 50, etc., are gathered into the numbers tuple within the function. This allows the calculate_sum function to operate effectively regardless of how many numbers are provided. This pattern is useful in data science for functions that might aggregate data from a variable number of input arrays or columns.
**kwargsSimilarly, you might need a function to accept any number of keyword arguments (arguments passed in the format key=value). This is common when setting optional configuration parameters for a process or defining metadata. Python uses the double asterisk (**) syntax for this, conventionally **kwargs (keyword arguments).
When **kwargs is used in a function definition, Python collects any keyword arguments that don't match other defined parameter names into a dictionary. The keys of the dictionary are the argument names (as strings), and the values are the corresponding argument values.
Let's look at an example simulating a function that logs processing details:
def log_processing_step(step_name, status, **details):
"""Logs details about a data processing step."""
print(f"--- Log Entry ---")
print(f"Step: {step_name}")
print(f"Status: {status}")
if details:
print("Additional Details:")
for key, value in details.items():
print(f" {key}: {value}")
print(f"-----------------\n")
# Calling the function with different keyword arguments
log_processing_step("Data Loading", "Success", source_file="data.csv", rows_loaded=1500)
log_processing_step("Feature Scaling", "Completed", method="StandardScaler", columns_scaled=5)
log_processing_step("Model Training", "Warning", algorithm="LinearRegression") # No extra details
In the first call, source_file="data.csv" and rows_loaded=1500 are collected into the details dictionary: {'source_file': 'data.csv', 'rows_loaded': 1500}. In the second call, details becomes {'method': 'StandardScaler', 'columns_scaled': 5}. In the third call, details is an empty dictionary. This allows log_processing_step to handle varying levels of detail flexibly, a pattern often seen when configuring steps in libraries like Scikit-learn or defining parameters for plotting functions.
*args and **kwargsYou can use both *args and **kwargs in the same function definition to accept both arbitrary positional and keyword arguments. The standard order for parameters in a function definition is:
*args**kwargsHere's an example incorporating both:
def flexible_function(required_param, *args, default_kwarg='default', **kwargs):
"""Demonstrates using standard, *args, default keyword, and **kwargs."""
print(f"Required Param: {required_param}")
print(f"Default Kwarg: {default_kwarg}")
if args:
print(f"Positional Args (*args): {args}")
if kwargs:
print(f"Keyword Args (**kwargs): {kwargs}")
print("-" * 20)
flexible_function(10)
flexible_function(20, 100, 200)
flexible_function(30, default_kwarg='custom')
flexible_function(40, 101, 102, config_option='A', user_id=99)
flexible_function(50, config_option='B', user_id=100, default_kwarg='overridden')
Observe how different combinations of arguments are correctly assigned to required_param, args, default_kwarg, and kwargs.
The asterisk (*) and double asterisk (**) operators can also be used when calling a function. This allows you to "unpack" sequences (like lists or tuples) into positional arguments and dictionaries into keyword arguments.
def process_data(id, value, category):
"""A simple function expecting three specific arguments."""
print(f"Processing ID: {id}, Value: {value}, Category: {category}")
# Data stored in a list/tuple
data_list = [101, 55._5, 'Electronics']
process_data(*data_list) # Unpacks list into positional arguments
# Data stored in a dictionary
data_dict = {'id': 202, 'value': 78._9, 'category': 'Clothing'}
process_data(**data_dict) # Unpacks dictionary into keyword arguments
# Can also combine:
more_data_dict = {'value': 15._2, 'category': 'Books'}
process_data(303, **more_data_dict) # Pass 'id' positionally, others via dict
This unpacking mechanism is extremely useful when the arguments for a function call are generated dynamically or stored in data structures.
Understanding *args and **kwargs is significant for writing adaptable Python functions. In data science and machine learning, this flexibility allows you to create more generic tools, handle varying inputs gracefully, and configure complex operations like model training or data transformation pipelines with optional parameters, leading to more reusable and maintainable code.
Was this section helpful?
© 2026 ApX Machine LearningEngineered with