Python *args & **kwargs Guide
When you do not know in advance how many positional arguments a caller might pass, prefix a parameter with an asterisk: *args.
Python automatically packs all extra positional arguments into an immutable tuple named args:
# Calculate the sum and average of ANY number of arguments:
def calculate_statistics(*numbers):
if not numbers:
return 0, 0
total = sum(numbers)
avg = total / len(numbers)
return total, round(avg, 2)
# Call with 2, 4, or 6 arguments seamlessly:
tot1, avg1 = calculate_statistics(10, 20)
tot2, avg2 = calculate_statistics(10, 20, 30, 40)
tot3, avg3 = calculate_statistics(5, 15, 25, 35, 45, 55)
print(f"Call 1 -> Sum: {tot1}, Avg: {avg1}")
print(f"Call 2 -> Sum: {tot2}, Avg: {avg2}")
print(f"Call 3 -> Sum: {tot3}, Avg: {avg3}")
Inside calculate_statistics, numbers is a tuple (10, 20, 30, 40). You can loop over it, slice it, or pass it to sum() and len().
Prefixing a parameter with two asterisks (**kwargs) captures arbitrary named keyword arguments and packs them into a dictionary:
# Function accepting arbitrary user profile metadata:
def build_user_profile(user_id, **attributes):
profile = {"id": user_id}
# kwargs acts as a standard dictionary:
for key, value in attributes.items():
profile[key] = value
return profile
# Call with different optional keyword arguments:
user_a = build_user_profile(101, username="balaji", role="Backend Lead", country="India")
user_b = build_user_profile(102, username="alex", is_active=True)
print("Profile A:", user_a)
print("Profile B:", user_b)
Inside the function, attributes is a dictionary: {'username': 'balaji', 'role': 'Backend Lead', 'country': 'India'}.
When combining positional, default, *args, keyword-only, and **kwargs parameters, Python enforces a strict grammatical order:
def complex_logger(prefix, *messages, level="INFO", **metadata):
print(f"[{level}] {prefix}:", " ".join(messages))
if metadata:
print(" Additional Metadata:", metadata)
# Calling with mixed parameter types:
complex_logger(
"ServerAPI",
"Database connection established", "Cache warmed up",
level="SUCCESS",
ip="192.168.1.1", port=5432
)
*args must come before keyword-only arguments, and **kwargs must ALWAYS be the final parameter in the signature.
Creating a complete modular student grading system using *args for subject marks and **kwargs for extra academic credentials:
# Student Marks Management & Report Generator
def generate_student_report(name, roll_no, *marks, **extra_details):
total_marks = sum(marks)
max_possible = len(marks) * 100
percentage = (total_marks / max_possible) * 100 if max_possible > 0 else 0
# Determine grade:
if percentage >= 90: grade = "A+ ๐"
elif percentage >= 75: grade = "A โจ"
elif percentage >= 60: grade = "B ๐"
else: grade = "C โ ๏ธ"
print("=" * 45)
print(f"๐ STUDENT REPORT: {name} (Roll #{roll_no})")
print("=" * 45)
print(f"โข Subjects Count: {len(marks)}")
print(f"โข Total Score: {total_marks}/{max_possible}")
print(f"โข Percentage: {percentage:.2f}%")
print(f"โข Final Grade: {grade}")
if extra_details:
print("\n๐ Additional Student Info:")
for key, val in extra_details.items():
print(f" - {key.replace('_', ' ').title()}: {val}")
print("=" * 45)
# Test the student report generator:
generate_student_report(
"Balaji", 202601,
95, 88, 92, 85, 98,
branch="Computer Science", semester="6th Sem", college="JNTU"
)
The function accepts any number of subject marks (whether a student took 3 or 8 subjects) and any custom metadata dynamically.
Writing def func(**kwargs, extra): causes a SyntaxError: invalid syntax. **kwargs MUST always be the absolute last parameter in any Python function signature.
Write a function calculate_bill(customer_name, *item_prices, discount=0.10, **store_info) that prints an itemized checkout receipt.
def calculate_bill(customer, *prices, discount=0.10, **store_info):
subtotal = sum(prices)
discount_amount = subtotal * discount
final_total = subtotal - discount_amount
print(f"Customer: {customer} | Store: {store_info.get('store_name', 'SuperMart')}")
print(f"Items: {len(prices)} | Subtotal: Rs.{subtotal}")
print(f"Final Total (after {discount*100}% discount): Rs.{final_total:.2f}")
calculate_bill("Alex", 120, 450, 300, store_name="City Mega Mart", city="Hyderabad")
Q Can I name *args something else like *values?
Yes! The asterisk (*) is what activates tuple packing. You can name the variable *values or *items. However, *args and **kwargs are PEP 8 standard conventions.
Q How do I unpack a list or dictionary into a function call?
Use *my_list to unpack list elements as positional arguments, and **my_dict to unpack dictionary key-value pairs as keyword arguments.
Q Why are *args and **kwargs commonly used in Python Decorators?
Decorators wrap arbitrary functions with unknown signatures. Using (*args, **kwargs) allows the wrapper to forward all arguments to the original function safely.