Learn how to write Python functions that accept any number of positional and keyword arguments using *args and **kwargs.
What it is
In Python, *args and **kwargs are special syntax used in function definitions to pass a variable number of arguments. The names args and kwargs are conventions; the asterisks (*) and double asterisks (**) are what matter.
*args: Collects extra positional arguments into a tuple.**kwargs: Collects extra keyword arguments into a dictionary.
This allows functions to be flexible without hardcoding specific parameter counts or names.
Why it matters
- Wrapper Functions: Essential for decorators that need to pass through unknown arguments to the wrapped function.
- API Clients: Useful when building interfaces where users might want to pass arbitrary query parameters or headers.
- Configuration: Allows setting default values while accepting overrides via keywords.
- Extensibility: Future-proofing functions so they can handle new argument types without breaking existing calls.
Syntax or steps
The order of parameters in a function definition must follow this sequence:
- Standard positional arguments
*args(if present)- Keyword-only arguments (optional)
**kwargs(if present)
Inside the function body, args behaves like a tuple, and kwargs behaves like a dictionary.
Example
def log_event(event_type, *details, **metadata):
print(f"Event: {event_type}")
# Process positional details (tuple)
if details:
print("Details:", ", ".join(details))
# Process keyword metadata (dictionary)
if metadata:
for key, value in metadata.items():
print(f" {key}: {value}")
# Calling the function
log_event(
"UserLogin",
"IP:192.168.1.1",
"Device:Mobile",
user_id=101,
status="success"
)
Explanation:
"UserLogin"is assigned to the standard parameterevent_type."IP:192.168.1.1"and"Device:Mobile"are collected into thedetailstuple.user_id=101andstatus="success"are collected into themetadatadictionary.
Common mistakes
- Misunderstanding Types: Remember
argsis a tuple (immutable, ordered), not a list. You cannot append to it directly inside the function. - Ordering Errors: Placing
**kwargsbefore*argscauses a SyntaxError.*argsmust come before**kwargs. - Unpacking Confusion: When calling a function,
*listunpacks a list into positional args, and**dictunpacks a dict into keyword args. Do not confuse definition syntax with call syntax. - Overusing Flexibility: If you know exactly which arguments are needed, use explicit named parameters. It improves code readability and IDE support.
When to use it
Compare *args/**kwargs with explicit parameters:
| Scenario | Recommended Approach |
|---|---|
| Fixed number of known inputs | Explicit parameters (e.g., def add(a, b)) |
| Decorators or wrappers | *args, **kwargs |
| Aggregating data (e.g., summing numbers) | *args |
| Passing options to underlying libraries | **kwargs |
Practice
Guided Exercise: Write a function called create_profile that takes a required name, collects any additional strings into a tuple called skills, and collects any keyword arguments into a dictionary called preferences. Print all three.
Challenge: Modify the function to calculate the average of all numeric values passed in *args, ignoring non-numeric types.
Hint for Challenge: Use isinstance(arg, (int, float)) to filter the tuple before calculating.
Quick check
Question: What data type does **kwargs collect arguments into?
Answer: A dictionary.
Summary
*args and **kwargs provide essential flexibility for Python functions by capturing variable-length positional and keyword arguments as tuples and dictionaries, respectively. They are critical for writing robust decorators, wrappers, and APIs but should be used judiciously to maintain code clarity.