Skip to main content

*args and **kwargs: Accepting a Variable Number of Arguments

We've learned how to define functions with a fixed number of positional and keyword arguments. But what if you want to create a function that can accept any number of arguments? For example, a function that can sum two numbers, or five, or a hundred. This is where *args and **kwargs come in, providing the ultimate flexibility for your function definitions.


📚 Prerequisites

To fully grasp this concept, you should be comfortable with:

  • Defining and calling Python functions.
  • Python data types, especially tuples and dictionaries.
  • The difference between positional and keyword arguments.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • *args: How to capture a variable number of positional arguments into a tuple.
  • **kwargs: How to capture a variable number of keyword arguments into a dictionary.
  • The Order of Operations: The correct order for using standard arguments, *args, and **kwargs in a single function definition.
  • Unpacking: How to use the * and ** operators to pass arguments from a list/tuple or dictionary to a function.

🧠 Section 1: Handling Variable Positional Arguments with *args

The syntax *args in a function definition allows you to pass a variable number of positional arguments to your function. The single asterisk (*) is the important part; args is just a conventional name. These arguments are collected into a tuple.

Let's create the flexible sum_all function we imagined earlier.

# args_example.py

def sum_all(*numbers):
"""
Calculates the sum of all positional arguments passed to it.
'numbers' will be a tuple.
"""
print(f"Arguments received as a tuple: {numbers}")
total = 0
for num in numbers:
total += num
return total

# Call the function with different numbers of arguments
print(f"Sum 1: {sum_all(1, 2)}")
print(f"Sum 2: {sum_all(10, 20, 30, 40, 50)}")
print(f"Sum 3: {sum_all()}")

# Output:
# Arguments received as a tuple: (1, 2)
# Sum 1: 3
# Arguments received as a tuple: (10, 20, 30, 40, 50)
# Sum 2: 150
# Arguments received as a tuple: ()
# Sum 3: 0

Code Breakdown:

  1. def sum_all(*numbers): defines a function that can take any number of positional arguments.
  2. Inside the function, numbers is a tuple containing all the arguments that were passed (e.g., (1, 2)).
  3. We can then iterate over this tuple just like any other tuple to perform our calculation.

💻 Section 2: Handling Variable Keyword Arguments with **kwargs

Similarly, the **kwargs syntax allows your function to accept any number of keyword (or named) arguments. The double asterisk (**) is the key; kwargs (short for "keyword arguments") is the convention. These arguments are collected into a dictionary.

This is incredibly useful for functions that need to handle arbitrary attributes or configuration options.

# kwargs_example.py

def display_user_profile(**user_info):
"""
Displays user information passed as keyword arguments.
'user_info' will be a dictionary.
"""
print(f"Arguments received as a dictionary: {user_info}")
if 'name' not in user_info:
print("Error: 'name' is a required field.")
return

print("\n--- User Profile ---")
for key, value in user_info.items():
print(f"{key.title()}: {value}")
print("--------------------")


# Call the function with different keyword arguments
display_user_profile(name="Brenda", age=42, city="Metropolis")
display_user_profile(name="Carlos", occupation="Engineer")
display_user_profile(age=30) # Missing the 'name' field

# Output:
# Arguments received as a dictionary: {'name': 'Brenda', 'age': 42, 'city': 'Metropolis'}
#
# --- User Profile ---
# Name: Brenda
# Age: 42
# City: Metropolis
# --------------------
# Arguments received as a dictionary: {'name': 'Carlos', 'occupation': 'Engineer'}
#
# --- User Profile ---
# Name: Carlos
# Occupation: Engineer
# --------------------
# Arguments received as a dictionary: {'age': 30}
# Error: 'name' is a required field.

Code Breakdown:

  1. def display_user_profile(**user_info): defines a function that can take any number of keyword arguments.
  2. Inside the function, user_info is a dictionary containing all the keyword arguments (e.g., {'name': 'Brenda', 'age': 42}).
  3. We can then access the data using standard dictionary methods like .items() or by checking for keys.

🛠️ Section 3: Putting It All Together

You can combine regular arguments, *args, and **kwargs in a single function. When you do, they must appear in this specific order:

  1. Standard positional arguments
  2. *args
  3. **kwargs
# all_together_example.py

def process_order(order_id, *items, **customer_details):
"""Processes a customer order with required and optional details."""
print(f"Processing Order ID: {order_id}")

print("\nItems in Order:")
for item in items:
print(f"- {item}")

print("\nCustomer Details:")
for key, value in customer_details.items():
print(f"- {key.title()}: {value}")

process_order(
101,
"Laptop", "Mouse", "Keyboard",
name="Diana Prince",
shipping_address="25 Paradise Island"
)

# Output:
# Processing Order ID: 101
#
# Items in Order:
# - Laptop
# - Mouse
# - Keyboard
#
# Customer Details:
# - Name: Diana Prince
# - Shipping_Address: 25 Paradise Island

🚀 Section 4: The Reverse: Unpacking with * and **

The * and ** operators can also be used when calling a function. This is called unpacking.

  • * unpacks a list or tuple into positional arguments.
  • ** unpacks a dictionary into keyword arguments.
# unpacking_example.py

def create_point(x, y, z):
"""Creates a 3D point."""
print(f"Point created at (x={x}, y={y}, z={z})")

# Unpacking a list/tuple with *
coords_list = [10, 20, 30]
create_point(*coords_list)

# Unpacking a dictionary with **
coords_dict = {'x': 5, 'y': 15, 'z': 25}
create_point(**coords_dict)

# Output:
# Point created at (x=10, y=20, z=30)
# Point created at (x=5, y=15, z=25)

This is extremely useful when you have data in a list or dictionary and need to pass it to a function that expects individual arguments.


✨ Conclusion & Key Takeaways

*args and **kwargs are powerful tools that allow you to write highly flexible functions that can handle a wide variety of inputs. While you may not use them in every function you write, they are essential for building adaptable frameworks, decorators, and utilities.

Let's summarize the key takeaways:

  • *args for Positional: Captures an unlimited number of positional arguments into a tuple.
  • **kwargs for Keyword: Captures an unlimited number of keyword arguments into a dictionary.
  • Order is Key: In a function definition, the order must be: standard_args, *args, **kwargs.
  • Unpacking is the Opposite: Use * and ** during a function call to pass arguments from lists and dictionaries.

Challenge Yourself: Write a function called generate_html_tag that takes a tag_name as a required positional argument (e.g., "p", "h1", "div"). It should also accept any number of keyword arguments, which will be the HTML attributes (e.g., class_="intro", id="main"). The function should return a string representing the HTML tag. For example, generate_html_tag("p", class_="text-bold") should return <p class_="text-bold"></p>.


➡️ Next Steps

You've now unlocked a new level of function flexibility. Next, we will explore a concept that is crucial for understanding how variables are accessed within different parts of your program: "Scope: Local, Enclosing, Global, and Built-in (LEGB rule)."

Happy coding!


Glossary (Python Terms)

  • *args: A special syntax in a function definition to capture a variable number of positional arguments into a tuple.
  • **kwargs: A special syntax to capture a variable number of keyword arguments into a dictionary.
  • Unpacking: The process of using * or ** to pass items from an iterable (like a list or dictionary) as individual arguments into a function call.

Further Reading (Python Resources)