Python
Programming
kwargs
Function Arguments
Code Usage

What is the purpose and use of **kwargs?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Python, **kwargs lets a function accept extra keyword arguments that were not declared as named parameters. Inside the function, those arguments arrive as a dictionary. The main purpose is flexibility: wrappers can forward options, configuration helpers can accept overrides, and classes in cooperative inheritance chains can consume only the keyword arguments they care about.

That flexibility is useful, but it should be used deliberately. **kwargs is not automatically better than explicit parameters. It is most helpful when the set of accepted keyword options is intentionally open or when arguments need to be passed through to something else.

What **kwargs Actually Means

In a function definition, **kwargs collects unmatched keyword arguments.

python
1def show_options(**kwargs):
2    print(kwargs)
3
4show_options(color="blue", size="large")

Output:

python
{'color': 'blue', 'size': 'large'}

The variable name does not have to be kwargs. The important part is the **. The name kwargs is just the normal convention.

Why It Is Useful

Without **kwargs, every optional named argument would have to be listed explicitly in the function signature. That is fine for small stable APIs, but some functions are designed to accept a flexible set of named options.

Common cases include:

  • wrapper functions that forward options
  • decorators that pass through arguments
  • helper functions that merge defaults with overrides
  • class hierarchies where different classes consume different named parameters

In those cases, **kwargs makes the function easier to extend without constantly changing its signature.

A Simple Configuration Example

python
1def build_config(**kwargs):
2    config = {
3        "verbose": False,
4        "timeout": 30,
5        "mode": "safe",
6    }
7    config.update(kwargs)
8    return config
9
10settings = build_config(verbose=True, timeout=10)
11print(settings)

This pattern is common when a function has sensible defaults but still needs to accept a variable set of overrides.

Forwarding Keyword Arguments

One of the most important uses of **kwargs is forwarding options to another function.

python
1def log_and_call(func, *args, **kwargs):
2    print("Calling", func.__name__)
3    return func(*args, **kwargs)
4
5def greet(name, punctuation="!"):
6    return f"Hello, {name}{punctuation}"
7
8print(log_and_call(greet, "Alice", punctuation="."))

This is common in adapters, decorators, and framework internals. The wrapper does not need to know every keyword parameter the wrapped function might accept.

Relationship to *args

*args collects extra positional arguments, while **kwargs collects extra keyword arguments.

python
1def demo(*args, **kwargs):
2    print("args:", args)
3    print("kwargs:", kwargs)
4
5demo(1, 2, 3, color="blue", enabled=True)

Together they let Python functions accept a very flexible calling pattern.

** in a Function Call Means Unpacking

There is a second related use of **: unpacking a dictionary into keyword arguments.

python
1def connect(host, port, ssl=False):
2    print(host, port, ssl)
3
4options = {"host": "localhost", "port": 5672, "ssl": True}
5connect(**options)

This is the inverse of collecting **kwargs. In a definition, Python gathers keyword arguments into a dictionary. In a call, Python expands a dictionary into keyword arguments.

**kwargs in Cooperative Inheritance

A common advanced use appears in class hierarchies where each class consumes only the parameters it needs and forwards the rest.

python
1class Named:
2    def __init__(self, *, name, **kwargs):
3        self.name = name
4        super().__init__(**kwargs)
5
6class Aged:
7    def __init__(self, *, age, **kwargs):
8        self.age = age
9        super().__init__(**kwargs)
10
11class Person(Named, Aged):
12    def __init__(self, *, name, age):
13        super().__init__(name=name, age=age)
14
15p = Person(name="Alice", age=10)
16print(p.name, p.age)

This pattern is especially useful with multiple inheritance and mixins.

Common Pitfalls

The biggest mistake is using **kwargs everywhere and making the API harder to discover than it needs to be. Another is accepting arbitrary keyword arguments without validating the keys, which turns flexibility into silent bugs. Developers also often confuse collection and unpacking: def f(**kwargs) collects keyword arguments, while f(**data) unpacks a dictionary. Finally, if the accepted keywords are stable and well known, explicit named parameters are often clearer than a catch-all dictionary.

Summary

  • '**kwargs collects extra keyword arguments into a dictionary.'
  • It is useful for wrappers, flexible configuration, and cooperative inheritance.
  • It works naturally alongside *args for positional arguments.
  • '** in a function call unpacks a dictionary into keyword arguments.'
  • Use **kwargs when flexibility helps, not as a default substitute for clear explicit parameters.

Course illustration
Course illustration

All Rights Reserved.