kwargs
Python programming
Python tips
function arguments
Python tutorials

Proper way to use kwargs in Python

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Python, **kwargs allows a function to accept any number of keyword arguments as a dictionary. The name kwargs is a convention — the ** syntax is what matters. This feature is essential for writing flexible APIs, forwarding arguments to other functions, decorators, and class inheritance. Understanding when and how to use **kwargs properly prevents common bugs like passing unexpected arguments or losing type safety.

Basic Usage

python
1def greet(**kwargs):
2    name = kwargs.get("name", "World")
3    greeting = kwargs.get("greeting", "Hello")
4    print(f"{greeting}, {name}!")
5
6greet(name="Alice", greeting="Hi")  # Hi, Alice!
7greet(name="Bob")                    # Hello, Bob!
8greet()                              # Hello, World!

**kwargs collects all keyword arguments not matched by named parameters into a dictionary. Inside the function, access values using kwargs.get(key, default) or kwargs[key].

Combining with Positional and Regular Keyword Args

python
1def create_user(name, age, **kwargs):
2    user = {"name": name, "age": age}
3    user.update(kwargs)  # merge any extra keyword arguments
4    return user
5
6user = create_user("Alice", 30, email="[email protected]", role="admin")
7print(user)
8# {'name': 'Alice', 'age': 30, 'email': '[email protected]', 'role': 'admin'}

The parameter order must be: positional arguments, *args, keyword arguments, **kwargs.

python
1def example(a, b, *args, key1="default", **kwargs):
2    print(f"a={a}, b={b}, args={args}, key1={key1}, kwargs={kwargs}")
3
4example(1, 2, 3, 4, key1="custom", extra="value")
5# a=1, b=2, args=(3, 4), key1=custom, kwargs={'extra': 'value'}

Forwarding Arguments to Another Function

python
1def log_message(message, **kwargs):
2    print(f"LOG: {message}")
3    send_notification(message, **kwargs)
4
5def send_notification(message, email=None, sms=None, slack_channel=None):
6    if email:
7        print(f"Email to {email}: {message}")
8    if sms:
9        print(f"SMS to {sms}: {message}")
10    if slack_channel:
11        print(f"Slack #{slack_channel}: {message}")
12
13log_message("Server down", email="[email protected]", slack_channel="alerts")

Use **kwargs to forward keyword arguments to downstream functions without explicitly listing every parameter. This is the most common and powerful use case.

Using kwargs in Decorators

python
1import functools
2import time
3
4def timing_decorator(func):
5    @functools.wraps(func)
6    def wrapper(*args, **kwargs):
7        start = time.perf_counter()
8        result = func(*args, **kwargs)
9        elapsed = time.perf_counter() - start
10        print(f"{func.__name__} took {elapsed:.4f}s")
11        return result
12    return wrapper
13
14@timing_decorator
15def fetch_data(url, timeout=30, retries=3):
16    time.sleep(0.1)  # simulate work
17    return f"Data from {url}"
18
19fetch_data("https://api.example.com", timeout=10)

Decorators use *args, **kwargs to accept and forward any arguments to the wrapped function without knowing its signature in advance.

Using kwargs in Class Inheritance

python
1class Animal:
2    def __init__(self, name, species, **kwargs):
3        self.name = name
4        self.species = species
5        super().__init__(**kwargs)  # forward remaining kwargs up the MRO
6
7class Pet(Animal):
8    def __init__(self, owner, **kwargs):
9        self.owner = owner
10        super().__init__(**kwargs)
11
12pet = Pet(owner="Alice", name="Rex", species="Dog")
13print(f"{pet.name} ({pet.species}) owned by {pet.owner}")
14# Rex (Dog) owned by Alice

Using **kwargs with super().__init__() enables cooperative multiple inheritance by passing unused arguments up the method resolution order (MRO).

Unpacking a Dictionary as Keyword Arguments

python
1config = {"host": "localhost", "port": 5432, "database": "mydb"}
2
3def connect(host, port, database):
4    print(f"Connecting to {database} at {host}:{port}")
5
6# Unpack dict into keyword arguments
7connect(**config)
8# Connecting to mydb at localhost:5432
9
10# Merging dictionaries and passing as kwargs
11defaults = {"timeout": 30, "retries": 3}
12overrides = {"timeout": 10}
13merged = {**defaults, **overrides}
14connect(**config, **merged)  # Error: unexpected keyword arguments
15# Instead, design the receiving function to accept **kwargs

The ** operator unpacks a dictionary into keyword arguments at the call site. This is the inverse of **kwargs in function definitions.

Validating kwargs

python
1def configure(name, **kwargs):
2    valid_keys = {"timeout", "retries", "verbose", "log_level"}
3    invalid = set(kwargs.keys()) - valid_keys
4    if invalid:
5        raise TypeError(f"Unexpected keyword arguments: {invalid}")
6
7    timeout = kwargs.get("timeout", 30)
8    verbose = kwargs.get("verbose", False)
9    print(f"Configured {name}: timeout={timeout}, verbose={verbose}")
10
11configure("app", timeout=10, verbose=True)    # OK
12# configure("app", typo_arg=5)                # TypeError: Unexpected keyword arguments: {'typo_arg'}

Since **kwargs accepts anything, validate keys explicitly to catch typos and invalid arguments early.

Common Pitfalls

  • Accepting **kwargs when explicit parameters are better: Using **kwargs hides the function's expected arguments from callers, IDE autocompletion, and type checkers. Only use **kwargs when the set of arguments is genuinely dynamic or for forwarding — prefer named parameters for known arguments.
  • Forgetting to forward **kwargs in decorators or inheritance: If a decorator wrapper uses *args, **kwargs but forgets to pass **kwargs to the wrapped function, keyword arguments are silently dropped. Always call func(*args, **kwargs) inside the wrapper.
  • Mutating kwargs directly: Modifying the kwargs dictionary (e.g., kwargs.pop("key")) changes the dict in place. If the caller reuses the dictionary, they see the modification. Use kwargs.get() or make a copy first.
  • Name collision between explicit params and kwargs: If a function has def f(name, **kwargs) and the caller passes name both positionally and as a keyword in a dict, Python raises TypeError: got multiple values for argument. Validate or separate the namespaces.
  • Not validating kwargs keys: Since **kwargs accepts any key, typos in argument names are silently ignored rather than raising errors. Always validate against a set of expected keys or use explicit parameters.

Summary

  • **kwargs collects extra keyword arguments into a dictionary
  • Parameter order: positional, *args, keyword-only, **kwargs
  • Primary use cases: argument forwarding, decorators, and cooperative inheritance
  • Use **dict to unpack a dictionary into keyword arguments at the call site
  • Validate kwargs keys explicitly to catch typos and unexpected arguments
  • Prefer named parameters over **kwargs when the argument set is known

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.