Of course! Let's break down *args and **kwargs in Python. They are powerful features that allow functions to accept a variable number of arguments.
The Core Idea
*argsallows a function to accept an arbitrary number of positional arguments. These arguments are collected into a tuple.**kwargsallows a function to accept an arbitrary number of keyword arguments (i.e., arguments passed askey=value). These arguments are collected into a dictionary.
The names args and kwargs are just conventions. You could use *my_args or **my_kwargs, but *args and **kwargs are the standard and universally understood.
*args (Positional Variable Arguments)
The (asterisk) is the key. It "unpacks" or collects all the remaining positional arguments into a tuple.
How to Use It
Place *args as the last parameter in a function's definition (unless you have other parameters with default values after it).
Example
def print_all_students(*student_names):
"""Prints all student names passed to the function."""
print(f"Type of student_names: {type(student_names)}")
print("List of Students:")
for name in student_names:
print(f"- {name}")
# Calling the function with different numbers of arguments
print_all_students("Alice", "Bob")
print_all_students("Charlie", "David", "Eve", "Frank")
Output:
Type of student_names: <class 'tuple'>
List of Students:
- Alice
- Bob
Type of student_names: <class 'tuple'>
List of Students:
- Charlie
- David
- Eve
- Frank
As you can see, student_names becomes a tuple containing all the positional arguments passed to the function.
A More Practical Example: Summing Numbers
def calculate_sum(*numbers):
"""Calculates the sum of all numbers passed to it."""
total = 0
for num in numbers:
total += num
return total
print(calculate_sum(1, 2)) # Output: 3
print(calculate_sum(10, 20, 30, 40)) # Output: 100
**kwargs (Keyword Variable Arguments)
The (double asterisk) is the key. It collects all the remaining keyword arguments into a dictionary.
How to Use It
Place **kwargs as the last parameter in a function's definition.
Example
def display_student_info(**student_details):
"""Prints all the details of a student passed as keyword arguments."""
print(f"Type of student_details: {type(student_details)}")
print("Student Information:")
for key, value in student_details.items():
print(f"{key}: {value}")
# Calling the function with different keyword arguments
display_student_info(name="Alice", age=21, major="Computer Science")
display_student_info(name="Bob", id=12345, is_honors=True, gpa=3.8)
Output:
Type of student_details: <class 'dict'>
Student Information:
name: Alice
age: 21
major: Computer Science
Type of student_details: <class 'dict'>
Student Information:
name: Bob
id: 12345
is_honors: True
gpa: 3.8
As you can see, student_details becomes a dictionary where the keys are the argument names and the values are the corresponding values.
A More Practical Example: Building an HTML Tag
def create_html_tag(tag_name, content, **attributes):
"""Creates an HTML tag with specified attributes."""
attrs_str = ""
for key, value in attributes.items():
attrs_str += f' {key}="{value}"'
return f"<{tag_name}{attrs_str}>{content}</{tag_name}>"
# Building a link tag
link = create_html_tag("a", "Click Here!", href="https://python.org", target="_blank", class="btn-primary")
print(link)
# Building an image tag
img = create_html_tag("img", src="logo.png", alt="Python Logo", width="200")
print(img)
Output:
<a href="https://python.org" target="_blank" class="btn-primary">Click Here!</a> <img src="logo.png" alt="Python Logo" width="200">
Combining *args, **kwargs, and Regular Parameters
You can use *args and **kwargs together with regular parameters. The order is important:
- Standard parameters (e.g.,
name) *args(for positional arguments)**kwargs(for keyword arguments)
Example
def process_data(name, *scores, **details):
"""Processes a student's data with a name, scores, and other details."""
print(f"Student Name: {name}")
print(f"Scores: {scores}") # This is a tuple
print(f"Details: {details}") # This is a dictionary
print("-" * 20)
# Calculate average score
if scores:
average = sum(scores) / len(scores)
print(f"Average Score: {average:.2f}")
print("-" * 20)
# Calling the function
process_data("Alice", 88, 92, 95, status="active", semester="Fall 2025")
process_data("Bob", 75, 80, status="inactive", lab_score=90)
Output:
Student Name: Alice
Scores: (88, 92, 95)
Details: {'status': 'active', 'semester': 'Fall 2025'}
--------------------
Average Score: 91.67
--------------------
Student Name: Bob
Scores: (75, 80)
Details: {'status': 'inactive', 'lab_score': 90}
--------------------
Average Score: 77.50
--------------------
Unpacking *args and **kwargs
The same and syntax can be used in function calls to "unpack" iterables (like lists/tuples) and dictionaries into positional and keyword arguments, respectively. This is the reverse of what happens in the function definition.
Unpacking with
def describe_pet(animal_type, pet_name):
print(f"I have a {animal_type}.")
print(f"Its name is {pet_name.title()}.")
my_list = ['hamster', 'harry']
describe_pet(*my_list) # Unpacks the list into positional arguments
Output:
I have a hamster.
Its name is Harry.
This is equivalent to calling describe_pet('hamster', 'harry').
Unpacking with
def build_profile(first, last, **user_info):
profile = {'first_name': first, 'last_name': last}
profile.update(user_info)
return profile
user_dict = {
'location': 'San Francisco',
'field': 'Data Science',
'age': 30
}
# Unpack the dictionary into keyword arguments
profile = build_profile('john', 'doe', **user_dict)
print(profile)
Output:
{'first_name': 'john', 'last_name': 'doe', 'location': 'San Francisco', 'field': 'Data Science', 'age': 30}
Summary Table
| Feature | *args |
**kwargs |
|---|---|---|
| Purpose | Accepts a variable number of positional arguments. | Accepts a variable number of keyword arguments. |
| Syntax | *args |
**kwargs |
| Data Type | Arguments are collected into a tuple. | Arguments are collected into a dictionary. |
| Usage in Definition | def my_func(*args): |
def my_func(**kwargs): |
| Usage in Call | my_func(*my_list) (unpacks a list/tuple) |
my_func(**my_dict) (unpacks a dictionary) |
| Order in Definition | Must come after standard parameters and before **kwargs. |
Must be the last parameter in the function definition. |
