0%

32- *args and **kwargs

Write flexible functions that accept any number of arguments. Master variable-length parameters for cleaner, more dynamic code.

What if you do not know how many arguments a function will receive? A sum function should work with 2 numbers or 10 numbers. A print function should accept any number of values. A configuration function should accept any number of named settings. Python gives you two special syntaxes for this: *args and **kwargs. The asterisk * collects extra positional arguments into a tuple. The double asterisk ** collects extra keyword arguments into a dictionary. These tools let you write functions that are flexible, reusable, and elegant.

🕯️ Magic Note

The names args and kwargs are conventions. The real syntax is the asterisk * and double asterisk **. You could name them *numbers and **options. Python only cares about the stars. But follow the convention. Other Python programmers expect *args and **kwargs.

What Are *args?
*args collects any number of extra positional arguments into a tuple. Use it when you want a function to accept a variable number of arguments.

Python

# Sum any number of arguments

def sum_all(*args):

return sum(args)

print(sum_all(1, 2, 3)) # 6

print(sum_all(10, 20)) # 30

print(sum_all(1, 2, 3, 4, 5)) # 15

print(sum_all()) # 0

💡 Inside the function, args is a tuple. You can loop over it, index it, or pass it to other functions that expect a sequence.
Combining Regular Parameters with *args
You can mix regular parameters with *args. The regular parameters come first, then *args collects the rest.

Python

# First parameter is required, then any number of extras

def greet(greeting, *names):

for name in names:

print(f”{greeting}, {name}!”)

greet(“Hello”, “Ali”, “Sara”, “Reza”)

# Hello, Ali!

# Hello, Sara!

# Hello, Reza!

def multiply(multiplier, *numbers):

return [num * multiplier for num in numbers]

print(multiply(3, 1, 2, 3, 4, 5)) # [3, 6, 9, 12, 15]

⚠️ The *args parameter must come after all regular positional parameters. Otherwise, it will swallow arguments meant for later parameters.
What Are **kwargs?
**kwargs collects any number of extra keyword arguments into a dictionary. Use it when you want a function to accept arbitrary named parameters.

Python

# Print any keyword arguments passed

def print_info(**kwargs):

for key, value in kwargs.items():

print(f”{key}: {value}”)

print_info(name=”Ali”, age=25, city=”Tehran”)

# name: Ali

# age: 25

# city: Tehran

def create_user(username, **details):

user = {“username”: username}

user.update(details)

return user

user = create_user(“ali123″, age=25, city=”Tehran”, is_active=True)

print(user) # {‘username’: ‘ali123’, ‘age’: 25, ‘city’: ‘Tehran’, ‘is_active’: True}

🕯️ Magic Note

The keys in **kwargs must be valid Python identifiers (no spaces, cannot start with a number). They become keys in a dictionary. This is perfect for optional configuration options.

Combining *args and **kwargs
You can use both *args and **kwargs in the same function. The order matters: first regular parameters, then *args, then **kwargs.

Python

def flexible_function(a, b=10, *args, **kwargs):

print(f”a: {a}”)

print(f”b: {b}”)

print(f”args: {args}”)

print(f”kwargs: {kwargs}”)

flexible_function(1, 20, 30, 40, 50, name=”Ali”, age=25)

# a: 1

# b: 20

# args: (30, 40, 50)

# kwargs: {‘name’: ‘Ali’, ‘age’: 25}

Unpacking with * and ** (The Reverse Operation)
Just as * and ** collect arguments in function definitions, they can also unpack sequences and dictionaries in function calls.

Python

# Unpacking a list into positional arguments

def add(a, b, c):

return a + b + c

numbers = [1, 2, 3]

result = add(*numbers) # Equivalent to add(1, 2, 3)

print(result) # 6

# Unpacking a dictionary into keyword arguments

def display(name, age, city):

print(f”{name} is {age} from {city}”)

person = {“name”: “Ali”, “age”: 25, “city”: “Tehran”}

display(**person) # Equivalent to display(name=”Ali”, age=25, city=”Tehran”)

# Mixing unpacking with regular arguments

def greet(greeting, name):

print(f”{greeting}, {name}!”)

data = [“Hello”, “Ali”]

greet(*data) # Hello, Ali!

🕯️ Magic Note

Unpacking with * and ** in function calls is the mirror image of collecting with *args and **kwargs in function definitions. The same syntax does opposite things depending on context.

Real-World Example: Logging Function
A flexible logging function that accepts any number of values and optional extra parameters.

Python

from datetime import datetime

def log_message(level, *messages, **options):

“””

Log messages with a level and optional settings.

Parameters:

level (str): Log level (INFO, WARNING, ERROR)

*messages: Variable number of message strings

**options: Optional settings like write_to_file, show_timestamp

“””

show_time = options.get(“show_timestamp”, True)

write_file = options.get(“write_to_file”, False)

if show_time:

timestamp = datetime.now().strftime(“%Y-%m-%d %H:%M:%S”)

prefix = f”[{timestamp}] [{level}]”

else:

prefix = f”[{level}]”

for msg in messages:

log_line = f”{prefix} {msg}”

print(log_line)

if write_file:

with open(“app.log”, “a”) as f:

f.write(log_line + “\n”)

# Usage examples

log_message(“INFO”, “Application started”)

log_message(“WARNING”, “Low disk space”, “Cleaning cache”, write_to_file=True)

log_message(“ERROR”, “Connection failed”, show_timestamp=False)

log_message(“DEBUG”, “Debug 1”, “Debug 2”, “Debug 3”, write_to_file=True, show_timestamp=True)

Real-World Example: Function Decorators
Decorators often use *args and **kwargs to wrap any function regardless of its signature.

Python

import time

def timer(func):

“””Decorator that times how long a function takes to run.”””

def wrapper(*args, **kwargs):

start = time.time()

result = func(*args, **kwargs) # Pass all arguments through

end = time.time()

print(f”{func.__name__} took {end – start:.4f} seconds”)

return result

return wrapper

@timer

def slow_function(a, b, c=1):

time.sleep(1)

return a + b + c

@timer

def another_function(name, *friends):

time.sleep(0.5)

return f”{name} has friends: {friends}”

# The same decorator works on functions with different signatures

print(slow_function(5, 10, c=20))

print(another_function(“Ali”, “Sara”, “Reza”))

🕯️ Magic Note

Decorators are one of the most powerful uses of *args and **kwargs. They allow you to wrap any function without knowing its parameters. You will learn more about decorators in Lesson 46.

Real-World Example: Class Inheritance
When overriding a method but still want to call the parent, use *args and **kwargs to pass through unknown arguments.

Python

class Animal:

def __init__(self, name, species, **kwargs):

self.name = name

self.species = species

for key, value in kwargs.items():

setattr(self, key, value)

class Dog(Animal):

def __init__(self, name, **kwargs):

super().__init__(name, species=”dog”, **kwargs)

class Cat(Animal):

def __init__(self, name, **kwargs):

super().__init__(name, species=”cat”, **kwargs)

# Each subclass can accept extra attributes without redefining them

buddy = Dog(“Buddy”, age=5, color=”brown”, owner=”Ali”)

print(buddy.age, buddy.color, buddy.owner) # 5 brown Ali

whiskers = Cat(“Whiskers”, age=3, favorite_toy=”yarn”)

print(whiskers.age, whiskers.favorite_toy) # 3 yarn

When NOT to Use *args and **kwargs
These tools are powerful but not always appropriate. Use them when you need flexibility. Avoid them when the function signature should be explicit.
  • Do not use *args if the number of arguments is fixed and small. Be explicit.
  • Do not use **kwargs as a way to hide parameters. Document expected keys.
  • Do not unpack *args into a function that expects specific types without checking.
  • Do not overuse. Functions with too many *args and **kwargs can be hard to understand.

Python

# Bad: Unnecessary use of *args

def add(*args): # This works but is unclear

if len(args) != 2:

raise ValueError(“Expected exactly 2 arguments”)

return args[0] + args[1]

# Good: Explicit parameters

def add(a, b): # Clear, self-documenting

return a + b

Common Mistakes with *args and **kwargs
  • Forgetting the asterisk: def func(args) is a single parameter named args, not variable arguments
  • Putting **kwargs before *args (SyntaxError)
  • Trying to use *args and **kwargs multiple times in one function
  • Not knowing that args is a tuple (immutable)
  • Mutating **kwargs inside the function (it is a regular dictionary, so you can, but be careful)
  • Confusing unpacking in function calls with collecting in function definitions

Python

# Common mistake: Missing asterisk

def bad_sum(args): # This takes ONE argument named args

return sum(args)

# Usage would be: bad_sum([1, 2, 3]) – not multiple arguments

# Correct: With asterisk

def good_sum(*args): # This takes any number of arguments

return sum(args)

# Usage: good_sum(1, 2, 3)

Check Your Understanding
  • What is the difference between *args and **kwargs?
  • Write a function that accepts any number of arguments and returns their average.
  • How do you unpack a list into positional arguments when calling a function?
  • What is the correct order of parameters in a function that uses *args and **kwargs?
  • Write a function that accepts a required name, any number of friends, and any number of extra info as keyword arguments.
  • Why do decorators often use *args and **kwargs?

⚡ Whisper

Two stars, one star. They look like magic symbols. And in a way, they are. The single star opens a door to many values. It gathers them into a tuple, a quiet line of waiting arguments. The double star opens another door. It gathers named values into a dictionary, pairs of keys and secrets. Together they make your functions boundless. They can accept anything the caller throws at them. But with great power comes great responsibility. Use these stars when you truly need flexibility. When you are wrapping another function. When you are passing through to a parent class. When the number of arguments is truly unknown. But for everyday functions, be explicit. Name your parameters. Let your function signature tell a story. A balance between flexibility and clarity is the mark of a master. Too many stars and the sky becomes confusing. Too few and the sky feels empty. Find your balance.

Related posts