
Python
Use `**kwargs` to accept any number of named (keyword) arguments. The double asterisk unpacks keyword arguments into a dictionary, enabling functions to accept arbitrary options and configurations. This pattern is essential for building flexible APIs and decorator patterns.
The `**kwargs` syntax captures all keyword arguments into a dictionary. The double asterisk operator unpacks named arguments, giving you a dictionary where keys are argument names and values are the provided values. This enables accepting unlimited optional configuration options.
def create_config(**settings):
for key, value in settings.items():
print(f"{key}: {value}")
create_config(
model="GPT-4",
temperature=0.8,
max_tokens=1000,
stream=True
)
# Output:
# model: GPT-4
# temperature: 0.8
# max_tokens: 1000
# stream: TrueThe `` unpacks named arguments into a dictionary**:
def print_kwargs(**kwargs):
print(f"kwargs type: {type(kwargs)}")
print(f"kwargs value: {kwargs}")
print_kwargs(name="Alice", age=25, city="NYC")
# Output:
# kwargs type: <class 'dict'>
# kwargs value: {'name': 'Alice', 'age': 25, 'city': 'NYC'}You can mix required parameters, defaults, `*args`, and `kwargs` together. The order is crucial: required → default → *args → kwargs.
def flexible_function(required, default_arg="default", *args, **kwargs):
print(f"Required: {required}")
print(f"Default: {default_arg}")
print(f"Args: {args}")
print(f"Kwargs: {kwargs}")
flexible_function("value", "custom", 1, 2, 3, name="test", debug=True)
# Output:
# Required: value
# Default: custom
# Args: (1, 2, 3)
# Kwargs: {'name': 'test', 'debug': True}Order matters: required → default → *args → **kwargs
Configuration systems use **kwargs extensively to accept arbitrary options while maintaining clean APIs.
def build_api_client(
base_url,
timeout=30,
**config
):
"""Build API client with flexible configuration."""
client_config = {
"base_url": base_url,
"timeout": timeout,
**config # Merge additional config options
}
return client_config
# Various usage patterns
config1 = build_api_client("https://api.example.com")
config2 = build_api_client("https://api.example.com", timeout=60, retries=3)
config3 = build_api_client(
"https://api.example.com",
auth_type="bearer",
ssl_verify=False
)Logging systems benefit from **kwargs to accept flexible metadata:
def log_event(event_type, *details, **metadata):
"""Log an event with flexible details and metadata."""
print(f"Event: {event_type}")
if details:
print(f" Details: {details}")
if metadata:
print(f" Metadata: {metadata}")
log_event(
"user_login",
"admin",
"success",
timestamp=1234567890,
ip="192.168.1.1"
)
# Output:
# Event: user_login
# Details: ('admin', 'success')
# Metadata: {'timestamp': 1234567890, 'ip': '192.168.1.1'}Wrapper functions use **kwargs to forward arguments transparently:
def call_function_multiple_times(func, times, *args, **kwargs):
"""Call a function multiple times with same arguments."""
results = []
for _ in range(times):
result = func(*args, **kwargs)
results.append(result)
return results
def add(a, b):
return a + b
results = call_function_multiple_times(add, 3, 5, 3)
print(results) # [8, 8, 8]def process_user(name, **options):
"""Process user with flexible options."""
# Extract with defaults
age = options.get('age', 18)
role = options.get('role', 'user')
active = options.get('active', True)
return {
"name": name,
"age": age,
"role": role,
"active": active
}
user1 = process_user("Alice")
user2 = process_user("Bob", age=25, role="admin")def create_record(**kwargs) -> dict:
"""Create record with type checking."""
allowed_keys = {'id', 'name', 'email', 'age'}
# Validate keys
invalid = set(kwargs.keys()) - allowed_keys
if invalid:
raise ValueError(f"Invalid keys: {invalid}")
return kwargs
# Valid
record = create_record(id=1, name="Alice", email="alice@example.com")
# Invalid - will raise error
# record = create_record(id=1, invalid_key="value")def merge_configs(base_config, **overrides):
"""Merge base config with overrides."""
config = base_config.copy()
config.update(overrides)
return config
defaults = {"timeout": 30, "retries": 3, "ssl": True}
custom = merge_configs(defaults, timeout=60, new_option="value")
# Result: {'timeout': 60, 'retries': 3, 'ssl': True, 'new_option': 'value'}def retry_decorator(max_attempts=3, delay=1, **options):
"""Decorator that retries function with options."""
def decorator(func):
def wrapper(*args, **kwargs):
import time
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
time.sleep(delay)
print(f"Retry {attempt + 1}/{max_attempts}")
return wrapper
return decorator
@retry_decorator(max_attempts=3, delay=0.5, verbose=True)
def unreliable_operation():
import random
if random.random() < 0.7:
raise Exception("Failed")
return "Success"def create_class(name, methods=None, **attributes):
"""Dynamically create a class with attributes and methods."""
if methods is None:
methods = {}
# Combine attributes and methods
namespace = {**attributes, **methods}
return type(name, (), namespace)
# Create class dynamically
Point = create_class(
"Point",
x=0,
y=0,
info=lambda self: f"({self.x}, {self.y})"
)
p = Point()
print(p.info()) # (0, 0)def api_call(url, method="GET", **options):
"""Make API call with required and optional parameters."""
required = {'timeout', 'retries'}
provided = set(options.keys())
missing = required - provided
if missing:
raise TypeError(f"Missing required options: {missing}")
return {"url": url, "method": method, **options}
# Valid
result = api_call("https://api.example.com", timeout=30, retries=3)
# Invalid - missing required options
# api_call("https://api.example.com")You can unpack dictionaries when calling functions:
def create_user(name, age, email):
return {"name": name, "age": age, "email": email}
user_data = {"name": "Alice", "age": 25, "email": "alice@example.com"}
user = create_user(**user_data) # Unpacks dict as kwargs| Concept | Remember |
|---|---|
| **kwargs | Capture variable keyword arguments |
| Dictionary | Arguments collected into dictionary |
| Order | Required → default → *args → **kwargs |
| Access | Use .get(), .items(), or direct keys |
| Flexible APIs | Perfect for configuration and options |
| Unpacking | Use `**dict` to unpack in calls |
You now understand all parameter types! Let's combine them in powerful patterns.
Ready to practice? Try advanced challenges
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward