Python Basics — *args & **kwargs
*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
*argscollects extra positional args as a tuple**kwargscollects extra keyword args as a dict- Order:
regular, *args, keyword_only, **kwargs *listunpacks iterables into function calls**dictunpacks dicts into keyword arguments- Essential for decorators, wrappers, and flexible APIs
Next up: Docstrings & Annotations →