070. '**Kwargs (Variable Keyword Arguments)'
Accept arbitrary named configuration options
070. ‘**Kwargs (Variable Keyword Arguments)’
Aryan’s monitor needs to be configurable without hardcoding every possible option as a parameter. **kwargs lets him accept arbitrary named settings:
def start_monitor(target, **options):
"""Start monitoring target with optional configuration."""
config = {
"interval": 5, # default polling interval
"warn_mb": 500,
"crit_mb": 1000,
}
config.update(options) # caller overrides win
print(f"Monitoring {target} | interval={config['interval']}s")
return config
# Minimal call
start_monitor("Chrome")
# Custom thresholds
start_monitor("PyCharm", warn_mb=300, crit_mb=800, interval=2)He also uses **kwargs for a wrapper that forwards all options to the underlying alert sender:
def send_alert(level, message, **delivery):
"""level + message are required; delivery options are flexible."""
return {
"level": level,
"message": message,
**delivery # unpack kwargs into the result dict
}
send_alert("CRITICAL", "Chrome at 1.2 GB", channel="slack", mention="@on-call")**kwargs is a dict inside the function — iterate with .items(), access with kwargs["key"], or unpack with **kwargs.
💡 Fun fact: The ** unpacking syntax in Python is powerful enough to merge two dicts in one expression: {**dict_a, **dict_b}. This pattern, used everywhere from config merging to API wrappers, was popularized by PEP 448 in Python 3.5. Before that, you needed dict_a.update(dict_b) which mutated the original.
⚠️ Watch out: **kwargs keys must be valid Python identifiers — you cannot do f(**{"my-key": 1}) because my-key is not a valid variable name. This trips up developers working with HTTP headers or JSON keys that use hyphens.
🤔 Think about it: def f(*args, **kwargs) accepts literally any combination of arguments. If you use this signature to build a wrapper function, what happens to type safety? How do popular Python libraries like requests handle this trade-off?
Learning objectives
- Use **kwargs to accept arbitrary keyword arguments
- Access **kwargs as a dict inside the function
- Combine *args and **kwargs for fully flexible functions
Key concepts
- **kwargs
- keyword arguments
- dict
Try it
Concept detail
**kwargs in a function definition collects extra keyword arguments into a dict.
def f(**kwargs): — kwargs is a dict, possibly empty def f(a, b, **rest): — a and b are normal params; rest gets extra keyword args
Inside the function: kwargs[“key”] — access a specific key kwargs.get(“key”, default) — safe access for k, v in kwargs.items() — iterate all pairs config.update(kwargs) — merge into another dict
Calling with a pre-built dict (unpacking): options = {“port”: 80, “debug”: True} f(**options) — same as f(port=80, debug=True)
Common patterns:
- Config builder: required params + **options for overrides
- Wrapper/forwarder: capture kwargs and pass to inner function
- Flexible logger: log(level, message, **context)
Combining *args and **kwargs: def f(*args, **kwargs): — accepts anything def wrapper(*args, **kwargs): return original(*args, **kwargs) # perfect forwarding
merge_dicts(**dicts): each keyword arg IS a dict — kwargs is {name: dict_value}. Iterate kwargs.values() to get the actual dicts to merge.
Solution
def build_config(name, **settings):
config = {"name": name}
config.update(settings)
return config
def merge_dicts(**dicts):
result = {}
for d in dicts.values():
result.update(d)
return result
def log_event(event_type, **details):
parts = [f"{k}={v}" for k, v in details.items()]
return f"EVENT {event_type}: {', '.join(parts)}"Tests
def test_build_config_basic():
config = build_config("myapp", host="localhost", port=8080)
assert config["name"] == "myapp"
assert config["host"] == "localhost"
assert config["port"] == 8080
def test_build_config_no_extras():
config = build_config("simple")
assert config == {"name": "simple"}
def test_build_config_many_extras():
config = build_config("app", a=1, b=2, c=3)
assert config["a"] == 1 and config["b"] == 2 and config["c"] == 3
def test_merge_dicts():
result = merge_dicts(a={"x": 1}, b={"y": 2})
assert result["x"] == 1
assert result["y"] == 2
def test_merge_dicts_overlap():
# Later dict wins on key conflict
result = merge_dicts(first={"key": "old"}, second={"key": "new"})
assert result["key"] == "new"
def test_log_event():
msg = log_event("login", user="alice", status="success")
assert "EVENT login:" in msg
assert "user=alice" in msg
assert "status=success" in msg