Python Basics — *args & **kwargs

2 minute read

*args & **kwargs

*args and **kwargs let functions accept a variable number of arguments.

*args — Variable Positional Arguments

*args collects extra positional arguments into a tuple:

def add(*args):
    print(type(args))  # <class 'tuple'>
    return sum(args)

print(add(1, 2))           # 3
print(add(1, 2, 3, 4, 5))  # 15
print(add())               # 0

With Regular Parameters

def greet(greeting, *names):
    for name in names:
        print(f"{greeting}, {name}!")

greet("Hello", "Alice", "Bob", "Charlie")
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!

**kwargs — Variable Keyword Arguments

**kwargs collects extra keyword arguments into a dictionary:

def print_info(**kwargs):
    print(type(kwargs))  # <class 'dict'>
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=30, city="NYC")
# name: Alice
# age: 30
# city: NYC

With Regular Parameters

def create_profile(name, **details):
    profile = {"name": name}
    profile.update(details)
    return profile

user = create_profile("Alice", age=30, city="NYC", role="admin")
print(user)
# {'name': 'Alice', 'age': 30, 'city': 'NYC', 'role': 'admin'}

Combining *args and **kwargs

def log(level, *args, **kwargs):
    message = " ".join(str(a) for a in args)
    details = ", ".join(f"{k}={v}" for k, v in kwargs.items())
    print(f"[{level}] {message}" + (f" ({details})" if details else ""))

log("INFO", "Server", "started")
# [INFO] Server started

log("ERROR", "Connection", "failed", host="localhost", port=8080)
# [ERROR] Connection failed (host=localhost, port=8080)

Parameter Order

def example(regular, *args, keyword_only, **kwargs):
    print(f"regular: {regular}")
    print(f"args: {args}")
    print(f"keyword_only: {keyword_only}")
    print(f"kwargs: {kwargs}")

example(1, 2, 3, keyword_only="kw", extra="value")
# regular: 1
# args: (2, 3)
# keyword_only: kw
# kwargs: {'extra': 'value'}

Order: regular → *args → keyword-only → **kwargs

Unpacking Arguments with * and **

Unpacking Lists/Tuples into Function Call

def add(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
print(add(*numbers))  # 6

# Same as add(1, 2, 3)

Unpacking Dicts into Function Call

def create_user(name, age, city):
    return f"{name}, {age}, {city}"

data = {"name": "Alice", "age": 30, "city": "NYC"}
print(create_user(**data))  # Alice, 30, NYC

# Same as create_user(name="Alice", age=30, city="NYC")

Combining Both

def display(a, b, c, x=0, y=0):
    print(f"a={a}, b={b}, c={c}, x={x}, y={y}")

args = [1, 2, 3]
kwargs = {"x": 10, "y": 20}
display(*args, **kwargs)
# a=1, b=2, c=3, x=10, y=20

Real-World Example: Decorator Pattern

def debug(func):
    def wrapper(*args, **kwargs):
        args_str = ", ".join(repr(a) for a in args)
        kwargs_str = ", ".join(f"{k}={v!r}" for k, v in kwargs.items())
        all_args = ", ".join(filter(None, [args_str, kwargs_str]))
        print(f"Calling {func.__name__}({all_args})")
        result = func(*args, **kwargs)
        print(f"→ {result}")
        return result
    return wrapper

@debug
def multiply(a, b):
    return a * b

multiply(3, 5)
# Calling multiply(3, 5)
# → 15

Common Patterns

Forwarding Arguments

def parent(*args, **kwargs):
    print("Parent called")
    child(*args, **kwargs)

def child(x, y, z=0):
    print(f"Child: x={x}, y={y}, z={z}")

parent(1, 2, z=3)
# Parent called
# Child: x=1, y=2, z=3

Merging Defaults with User Config

def configure(**overrides):
    defaults = {
        "host": "localhost",
        "port": 8080,
        "debug": False,
        "workers": 4,
    }
    config = {**defaults, **overrides}
    return config

print(configure(port=3000, debug=True))
# {'host': 'localhost', 'port': 3000, 'debug': True, 'workers': 4}

Summary

  • *args collects extra positional args as a tuple
  • **kwargs collects extra keyword args as a dict
  • Order: regular, *args, keyword_only, **kwargs
  • *list unpacks iterables into function calls
  • **dict unpacks dicts into keyword arguments
  • Essential for decorators, wrappers, and flexible APIs

Next up: Docstrings & Annotations →

Updated: