python
dictionary
function
kwargs
programming

Passing a dictionary to a function as keyword parameters

Master System Design with Codemia

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

Introduction

In Python, use the ** (double-splat) operator to unpack a dictionary as keyword arguments when calling a function: func(**my_dict). Each dictionary key becomes a parameter name and each value becomes the argument. The keys must be strings and must match the function's parameter names (or the function must accept **kwargs). This pattern is widely used for configuration passing, decorator forwarding, and building dynamic function calls.

Basic Usage

python
1def greet(name, greeting="Hello"):
2    print(f"{greeting}, {name}!")
3
4# Pass a dictionary as keyword arguments
5params = {"name": "Alice", "greeting": "Hi"}
6greet(**params)
7# Hi, Alice!
8
9# Equivalent to:
10greet(name="Alice", greeting="Hi")

The ** operator unpacks the dictionary so that params["name"] becomes name="Alice" and params["greeting"] becomes greeting="Hi".

Mixing Positional and Dictionary Arguments

python
1def create_user(name, age, email, role="user"):
2    print(f"{name} ({age}) - {email} [{role}]")
3
4# Positional + dictionary
5extra = {"email": "[email protected]", "role": "admin"}
6create_user("Alice", 30, **extra)
7# Alice (30) - [email protected] [admin]
8
9# Dictionary + explicit keyword
10config = {"name": "Bob", "age": 25}
11create_user(**config, email="[email protected]")
12# Bob (25) - [email protected] [user]

Using **kwargs to Accept Any Keywords

python
1def connect(**kwargs):
2    host = kwargs.get("host", "localhost")
3    port = kwargs.get("port", 5432)
4    db = kwargs.get("database", "mydb")
5    print(f"Connecting to {host}:{port}/{db}")
6
7# Pass configuration as a dictionary
8db_config = {
9    "host": "prod-db.example.com",
10    "port": 5432,
11    "database": "production",
12    "timeout": 30,
13}
14connect(**db_config)
15# Connecting to prod-db.example.com:5432/production

Forwarding Arguments Between Functions

A common pattern is accepting **kwargs in one function and forwarding them to another:

python
1import requests
2
3def fetch_data(url, **kwargs):
4    # Add default headers, then forward all other kwargs to requests.get
5    headers = kwargs.pop("headers", {})
6    headers.setdefault("User-Agent", "MyApp/1.0")
7    return requests.get(url, headers=headers, **kwargs)
8
9# The caller can pass any requests.get parameter
10response = fetch_data(
11    "https://api.example.com/data",
12    timeout=10,
13    params={"page": 1},
14)

Merging Dictionaries for Function Calls

python
1def send_email(to, subject, body, cc=None, bcc=None, priority="normal"):
2    print(f"To: {to}, Subject: {subject}, Priority: {priority}")
3
4# Merge default config with overrides
5defaults = {"priority": "low", "cc": "[email protected]"}
6overrides = {"to": "[email protected]", "subject": "Report", "body": "See attached"}
7
8# Python 3.9+ merge operator
9send_email(**{**defaults, **overrides})
10
11# Python 3.5+ unpacking
12send_email(**defaults, **overrides)

Building Dynamic Function Calls

python
1import json
2
3def process_order(product, quantity, price, discount=0):
4    total = quantity * price * (1 - discount)
5    return {"product": product, "total": round(total, 2)}
6
7# Load parameters from JSON configuration
8config_json = '{"product": "Widget", "quantity": 5, "price": 9.99, "discount": 0.1}'
9params = json.loads(config_json)
10
11result = process_order(**params)
12print(result)  # {'product': 'Widget', 'total': 44.96}

Filtering Dictionary Keys

If the dictionary has keys that do not match the function parameters, you get a TypeError. Filter first:

python
1import inspect
2
3def create_profile(name, age, email):
4    return {"name": name, "age": age, "email": email}
5
6# Dictionary has extra keys
7data = {"name": "Alice", "age": 30, "email": "[email protected]", "phone": "555-0123"}
8
9# Filter to only accepted parameters
10sig = inspect.signature(create_profile)
11filtered = {k: v for k, v in data.items() if k in sig.parameters}
12result = create_profile(**filtered)
13# {'name': 'Alice', 'age': 30, 'email': '[email protected]'}

Common Pitfalls

  • Dictionary keys do not match function parameter names: If the dictionary contains a key that is not a valid parameter name and the function does not accept **kwargs, Python raises TypeError: unexpected keyword argument. Verify keys match parameter names before unpacking.
  • Non-string dictionary keys: The ** operator requires all dictionary keys to be strings. A dictionary like {1: "a", 2: "b"} raises TypeError when unpacked. Convert keys to strings first if needed.
  • Passing the same argument twice: If you pass a keyword argument explicitly and it is also in the dictionary, Python raises TypeError: got multiple values for argument. Remove the key from the dictionary or do not pass it as a separate keyword.
  • Mutating the original dictionary with pop() inside the function: Using kwargs.pop("key") modifies the dictionary in place if it was passed directly. If the caller reuses the dictionary, keys will be missing. Use kwargs.get() for non-destructive access, or unpack a copy.
  • Confusing *args with **kwargs: * unpacks a list/tuple as positional arguments, while ** unpacks a dictionary as keyword arguments. Using * on a dictionary unpacks its keys as positional arguments, not key-value pairs.

Summary

  • Use func(**my_dict) to unpack a dictionary as keyword arguments
  • Dictionary keys must be strings and must match the function's parameter names
  • Use **kwargs in the function signature to accept arbitrary keyword arguments
  • Merge dictionaries with {**defaults, **overrides} before unpacking
  • Filter dictionary keys with inspect.signature when extra keys are present

Course illustration
Course illustration

All Rights Reserved.