Of course! Let's break down *args and **kwargs in Python. They are powerful features that allow your functions to accept a variable number of arguments.
The Core Idea
In short:
*argsallows you to pass a variable number of positional arguments.**kwargsallows you to pass a variable number of keyword arguments (i.e., arguments with names).
Think of them as a way to make your functions more flexible and generic.
*args (Positional Variable Arguments)
The (asterisk) before args tells Python to collect any extra positional arguments into a tuple.
How it Works
- You can name
argsanything, butargsis the conventional and widely understood name. - The is the important part.
Example: Summing Numbers
Let's create a function that can sum any number of values.
def sum_all(*args):
"""This function sums all the numbers passed to it."""
print(f"Arguments received: {args}") # This will be a tuple
print(f"Type of args: {type(args)}")
total = 0
for number in args:
total += number
return total
# --- Using the function ---
# Calling with 2 arguments
print(f"Sum of 5 and 10: {sum_all(5, 10)}")
# Output:
# Arguments received: (5, 10)
# Type of args: <class 'tuple'>
# Sum of 5 and 10: 15
print("-" * 20)
# Calling with 4 arguments
print(f"Sum of 1, 2, 3, and 4: {sum_all(1, 2, 3, 4)}")
# Output:
# Arguments received: (1, 2, 3, 4)
# Type of args: <class 'tuple'>
# Sum of 1, 2, 3, and 4: 10
print("-" * 20)
# Calling with no arguments
print(f"Sum of nothing: {sum_all()}")
# Output:
# Arguments received: ()
# Type of args: <class 'tuple'>
# Sum of nothing: 0
**kwargs (Keyword Variable Arguments)
The (double asterisk) before kwargs tells Python to collect any extra keyword arguments into a dictionary.
How it Works
- You can name
kwargsanything, butkwargsis the conventional name. - The is the crucial part.
- The keys in the dictionary will be the argument names (as strings), and the values will be the values you passed.
Example: Printing User Information
Let's create a function that prints details about a user, where we don't know all the possible details in advance.
def print_user_details(**kwargs):
"""This function prints details about a user from keyword arguments."""
print(f"Details received: {kwargs}") # This will be a dictionary
print(f"Type of kwargs: {type(kwargs)}")
print("\n--- User Profile ---")
for key, value in kwargs.items():
print(f"{key.replace('_', ' ').title()}: {value}")
print("---------------------\n")
# --- Using the function ---
# Calling with 3 keyword arguments
print_user_details(name="Alice", age=30, city="New York")
# Output:
# Details received: {'name': 'Alice', 'age': 30, 'city': 'New York'}
# Type of kwargs: <class 'dict'>
#
# --- User Profile ---
# Name: Alice
# Age: 30
# City: New York
# ---------------------
# Calling with different keyword arguments
print_user_details(username="bob_builder", account_status="active", member_since=2025)
# Output:
# Details received: {'username': 'bob_builder', 'account_status': 'active', 'member_since': 2025}
# Type of kwargs: <class 'dict'>
#
# --- User Profile ---
# Username: bob_builder
# Account Status: active
# Member Since: 2025
# ---------------------
# Calling with no arguments
print_user_details()
# Output:
# Details received: {}
# Type of kwargs: <class 'dict'>
#
# --- User Profile ---
#
# ---------------------
Combining *args, **kwargs, and Standard Arguments
You can use *args and **kwargs in the same function, along with standard positional and keyword arguments. The order is important and must follow this rule:
*Standard Arguments → `argskwargs`
Example: A Flexible Greeting Function
def make_greeting(greeting, *names, **details):
"""
Greets a list of people with a specific greeting and optional details.
- greeting: A standard positional argument.
- *names: A variable number of names to greet.
- **details: Optional keyword arguments for the greeting.
"""
print(f"Standard greeting: '{greeting}'")
print(f"Names to greet (args): {names}")
print(f"Optional details (kwargs): {details}")
print("\n--- Greetings ---")
for name in names:
# Get a detail if it exists, otherwise use a default
punctuation = details.get('punctuation', '!')
print(f"{greeting}, {name}{punctuation}")
print("-----------------")
# --- Using the function ---
make_greeting("Hello", "Charlie", "Diana", punctuation=".", enthusiasm_level="high")
# Output:
# Standard greeting: 'Hello'
# Names to greet (args): ('Charlie', 'Diana')
# Optional details (kwargs): {'punctuation': '.', 'enthusiasm_level': 'high'}
#
# --- Greetings ---
# Hello, Charlie.
# Hello, Diana.
# -----------------
# Here, 'punctuation' is not provided, so it defaults to '!'
make_greeting("Hi", "Eve", "Frank")
# Output:
# Standard greeting: 'Hi'
# Names to greet (args): ('Eve', 'Frank')
# Optional details (kwargs): {}
#
# --- Greetings ---
# Hi, Eve!
# Hi, Frank!
# -----------------
Unpacking: The Reverse Operation
and can also be used to unpack iterables (like lists or tuples) and dictionaries into a function call. This is the reverse of collecting them.
Unpacking with
def multiply(a, b, c):
return a * b * c
my_numbers = [2, 3, 4]
# Instead of multiply(my_numbers[0], my_numbers[1], my_numbers[2])
# We can use the * operator to unpack the list
result = multiply(*my_numbers)
print(f"The result is: {result}") # Output: The result is: 24
Unpacking with
def introduce(name, age, city):
return f"Hello, my name is {name}. I am {age} years old and I live in {city}."
person_info = {
"name": "Grace",
"age": 28,
"city": "London"
}
# Instead of introduce(person_info["name"], person_info["age"], person_info["city"])
# We can use the ** operator to unpack the dictionary
intro = introduce(**person_info)
print(intro) # Output: Hello, my name is Grace. I am 28 years old and I live in London.
Summary Table
| Feature | *args |
**kwargs |
|---|---|---|
| Full Name | Variable-length positional arguments | Variable-length keyword arguments |
| Syntax | *args |
**kwargs |
| How it's Used | In a function definition to collect extra arguments. | In a function definition to collect extra keyword arguments. |
| Data Structure | Tuple | Dictionary |
| Example Usage | def func(a, b, *args): |
def func(a, b, **kwargs): |
| Unpacking | *my_list (unpacks a list/tuple) |
**my_dict (unpacks a dictionary) |
| Common Use Case | When you don't know how many arguments will be passed. | When you want to accept named parameters and their values dynamically. |
By mastering *args and **kwargs, you can write more robust, flexible, and Pythonic functions.
