Day 14/100: Understanding *args and **kwargs in Python Functions



This content originally appeared on DEV Community and was authored by Rahul Gupta

Welcome to Day 14 of the 100 Days of Python series!
Today we’re going to unlock the magic behind flexible function arguments — *args and `kwargs`. These let your functions accept **any number of arguments, making your code more dynamic, reusable, and powerful.

📦 What You’ll Learn

  • What *args and **kwargs are
  • When and how to use them
  • How to combine them with regular arguments
  • Real-world examples

🧠 1. What Are *args?

*args lets your function accept any number of positional arguments as a tuple.

🔸 Example:

def add_numbers(*args):
    total = sum(args)
    print("Sum:", total)

add_numbers(1, 2)
add_numbers(10, 20, 30)

Output:

Sum: 3
Sum: 60

You can loop through args like a list:

def show_args(*args):
    for arg in args:
        print(arg)

🧠 2. What Are **kwargs?

**kwargs lets your function accept any number of keyword arguments (named arguments) as a dictionary.

🔸 Example:

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=30, city="New York")

Output:

name: Alice
age: 30
city: New York

🧪 3. Using Both Together

You can use *args and **kwargs in the same function:

def demo_function(*args, **kwargs):
    print("Positional arguments:", args)
    print("Keyword arguments:", kwargs)

demo_function(1, 2, 3, name="Alice", job="Engineer")

Output:

Positional arguments: (1, 2, 3)
Keyword arguments: {'name': 'Alice', 'job': 'Engineer'}

🔺 Always put *args before **kwargs in the function definition.

✨ 4. Default + Flexible Parameters

You can mix regular, *args, and **kwargs:

def send_email(to, subject, *attachments, **headers):
    print("To:", to)
    print("Subject:", subject)
    print("Attachments:", attachments)
    print("Headers:", headers)

send_email(
    "user@example.com",
    "Meeting Notes",
    "file1.pdf", "file2.png",
    priority="high", read_receipt=True
)

🔄 5. Unpacking with * and **

You can pass a list or dictionary into a function using * and **:

def greet(name, age):
    print(f"Hello {name}, you're {age} years old.")

info = {"name": "Bob", "age": 25}
greet(**info)  # Unpacks dictionary as keyword arguments
nums = [5, 10]
def multiply(x, y):
    print(x * y)

multiply(*nums)  # Unpacks list as positional arguments

🎯 Real-World Example: Logging

def log_event(event_type, *args, **kwargs):
    print(f"[{event_type.upper()}]")
    for arg in args:
        print(f"- Detail: {arg}")
    for key, val in kwargs.items():
        print(f"- {key}: {val}")

log_event(
    "error",
    "File not found", "User logged out",
    filename="report.pdf", user="admin"
)

🧼 Best Practices

  • ✅ Use *args when you’re not sure how many positional arguments will be passed.
  • ✅ Use **kwargs to accept any number of named arguments.
  • ✅ Use descriptive names instead of just args and kwargs for clarity (e.g., *numbers, **options).
  • ⚠ Don’t overuse them where explicit parameters make more sense.

🧠 Recap

Today you learned:

  • How *args collects extra positional arguments
  • How **kwargs collects extra keyword arguments
  • How to combine them with regular parameters
  • How to unpack values using * and **
  • Real-world examples like logging and flexible APIs


This content originally appeared on DEV Community and was authored by Rahul Gupta