Python
kwargs
function arguments
programming
code examples

pass kwargs argument to another function with kwargs

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 collects keyword arguments into a dictionary. To pass these arguments to another function, use the ** unpacking operator: other_function(**kwargs). This forwards all keyword arguments to the called function as if they were specified individually. This pattern is fundamental in Python for creating wrapper functions, decorators, and class hierarchies where functions need to accept and pass through arbitrary keyword arguments.

Basic Forwarding

python
1def inner_function(name, age, city="Unknown"):
2    print(f"{name}, {age}, from {city}")
3
4def outer_function(**kwargs):
5    # Forward all kwargs to inner_function
6    inner_function(**kwargs)
7
8outer_function(name="Alice", age=30, city="Seattle")
9# Alice, 30, from Seattle

**kwargs in the outer function collects name, age, and city into a dict {'name': 'Alice', 'age': 30, 'city': 'Seattle'}. Then **kwargs unpacks the dict back into keyword arguments for inner_function.

Adding or Modifying Arguments

python
1def log_request(url, method="GET", timeout=30, **kwargs):
2    print(f"{method} {url} (timeout={timeout})")
3    print(f"Extra options: {kwargs}")
4
5def make_request(url, **kwargs):
6    # Add default values, then forward everything
7    kwargs.setdefault("timeout", 60)
8    kwargs.setdefault("method", "POST")
9    log_request(url, **kwargs)
10
11make_request("https://api.example.com/data", headers={"Auth": "token"})
12# POST https://api.example.com/data (timeout=60)
13# Extra options: {'headers': {'Auth': 'token'}}

kwargs.setdefault(key, value) adds the key only if it is not already present, allowing callers to override defaults.

Combining *args and **kwargs

python
1def wrapper(*args, **kwargs):
2    print(f"Positional args: {args}")
3    print(f"Keyword args: {kwargs}")
4    return target_function(*args, **kwargs)
5
6def target_function(x, y, z=0, verbose=False):
7    result = x + y + z
8    if verbose:
9        print(f"{x} + {y} + {z} = {result}")
10    return result
11
12wrapper(1, 2, z=3, verbose=True)
13# Positional args: (1, 2)
14# Keyword args: {'z': 3, 'verbose': True}
15# 1 + 2 + 3 = 6

*args collects positional arguments as a tuple. **kwargs collects keyword arguments as a dict. Together they capture the entire call signature.

Decorator Pattern

python
1import time
2import functools
3
4def timer(func):
5    @functools.wraps(func)
6    def wrapper(*args, **kwargs):
7        start = time.time()
8        result = func(*args, **kwargs)  # forward all args and kwargs
9        elapsed = time.time() - start
10        print(f"{func.__name__} took {elapsed:.3f}s")
11        return result
12    return wrapper
13
14@timer
15def slow_function(n, message="Processing"):
16    print(message)
17    time.sleep(n)
18    return "done"
19
20slow_function(1, message="Working...")
21# Working...
22# slow_function took 1.001s

Decorators use *args, **kwargs to make the wrapper transparent — it accepts and forwards any combination of arguments the original function expects.

Class Inheritance with super()

python
1class Animal:
2    def __init__(self, name, sound, **kwargs):
3        self.name = name
4        self.sound = sound
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)  # forward name, sound, and any extras
11
12class Dog(Pet):
13    def __init__(self, breed, **kwargs):
14        self.breed = breed
15        super().__init__(**kwargs)
16
17dog = Dog(breed="Labrador", owner="Alice", name="Rex", sound="Woof")
18print(f"{dog.name} ({dog.breed}), owned by {dog.owner}")
19# Rex (Labrador), owned by Alice

Each class in the hierarchy consumes the kwargs it needs and forwards the rest via super().__init__(**kwargs). This is the cooperative multiple inheritance pattern.

Filtering kwargs Before Forwarding

python
1import inspect
2
3def safe_call(func, **kwargs):
4    """Call func with only the kwargs it accepts."""
5    sig = inspect.signature(func)
6    valid_keys = set(sig.parameters.keys())
7    filtered = {k: v for k, v in kwargs.items() if k in valid_keys}
8    return func(**filtered)
9
10def greet(name, greeting="Hello"):
11    return f"{greeting}, {name}!"
12
13# Extra kwargs are silently filtered out
14result = safe_call(greet, name="Alice", greeting="Hi", extra="ignored")
15print(result)  # Hi, Alice!

Use inspect.signature to find which parameters a function accepts, then filter kwargs to avoid TypeError: unexpected keyword argument.

Merging Multiple kwargs Sources

python
1def configure(**kwargs):
2    # Merge defaults with user-provided kwargs
3    defaults = {"host": "localhost", "port": 8080, "debug": False}
4    config = {**defaults, **kwargs}  # kwargs override defaults
5    print(config)
6
7configure(port=3000, debug=True)
8# {'host': 'localhost', 'port': 3000, 'debug': True}
9
10# Python 3.9+ also supports the | operator
11defaults = {"host": "localhost", "port": 8080}
12overrides = {"port": 3000, "debug": True}
13config = defaults | overrides
14# {'host': 'localhost', 'port': 3000, 'debug': True}

The {**a, **b} syntax merges two dicts, with the second overriding duplicate keys.

Common Pitfalls

  • Forgetting the ** when forwarding: Writing other_func(kwargs) passes the entire dict as a single positional argument. You must use other_func(**kwargs) to unpack it into keyword arguments.
  • Modifying kwargs and causing duplicate keyword arguments: If you do other_func(name="Alice", **kwargs) and kwargs also contains name, Python raises TypeError: got multiple values for argument 'name'. Remove the key from kwargs first with kwargs.pop('name', None).
  • Not using functools.wraps in decorators: Without @functools.wraps(func), the wrapper function loses the original function's name, docstring, and signature. Always apply wraps in decorator patterns.
  • Passing kwargs to functions that do not accept **kwargs: If the target function has fixed parameters and kwargs contains extra keys, Python raises TypeError: unexpected keyword argument. Filter kwargs first or ensure the target function accepts **kwargs.
  • Mutating the kwargs dict unintentionally: kwargs.pop() or kwargs['key'] = value modifies the dict in place. If the caller expects kwargs to be unchanged, create a copy first: local_kwargs = {**kwargs}.

Summary

  • Use **kwargs to collect keyword arguments into a dict and **kwargs again to unpack them when calling another function
  • This pattern is essential for decorators, wrapper functions, and cooperative inheritance with super()
  • Use kwargs.setdefault() to add default values without overriding caller-specified arguments
  • Filter kwargs with inspect.signature when the target function does not accept arbitrary keyword arguments
  • Merge multiple sources with {**defaults, **kwargs} — later dicts override earlier ones
  • Always use functools.wraps in decorators to preserve the wrapped function's metadata

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.