How to pass kwargs in Python
A complete guide to passing kwargs in Python. Learn different methods, get useful tips, see real-world examples, and debug common errors.

Python's **kwargs lets you pass a variable number of keyword arguments to a function. This feature makes your functions more flexible and is essential to build adaptable code.
Here, you'll learn techniques to use **kwargs effectively. You will also discover real-world applications and get practical advice to debug common issues you might encounter along the way.
Using **kwargs to accept arbitrary keyword arguments
def greet(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
greet(name="Alice", age=30, city="New York")--OUTPUT--name: Alice
age: 30
city: New York
The greet function uses **kwargs to capture any number of keyword arguments. This is why the function call works with name="Alice", age=30, and city="New York" even though they aren't formal parameters. The double-asterisk syntax packs these arguments into a dictionary that the function can then access.
Inside the function, kwargs is treated just like a standard dictionary. You can use familiar methods like .items() to iterate through the key-value pairs you've passed in, giving you a simple way to work with flexible inputs.
Basic kwargs techniques
Building on the idea that **kwargs is a dictionary, you'll now see how to access its values, unpack other dictionaries, and combine it with *args.
Accessing values from the kwargs dictionary
def process_user(**kwargs):
username = kwargs.get('username', 'Anonymous')
role = kwargs.get('role', 'User')
print(f"User {username} has role: {role}")
process_user(username="admin", role="Administrator")--OUTPUT--User admin has role: Administrator
The .get() method is a safe way to access values from the kwargs dictionary. It lets you look up a key and provide a default value if that key doesn't exist, which helps you avoid errors.
- In the
process_userfunction,kwargs.get('username', 'Anonymous')tries to find a'username'. If it can't, the function uses'Anonymous'instead. - This technique is great for handling optional arguments without causing a
KeyErrorif they aren't provided in the function call.
Unpacking dictionaries as kwargs
def create_profile(name, email, **extra):
print(f"Creating profile for {name} ({email})")
for key, value in extra.items():
print(f" {key}: {value}")
user_data = {"location": "San Francisco", "interests": ["Python", "AI"]}
create_profile("Bob", "[email protected]", **user_data)--OUTPUT--Creating profile for Bob ([email protected])
location: San Francisco
interests: ['Python', 'AI']
You can also unpack a dictionary's contents directly into a function's keyword arguments using the double-asterisk ** operator. In the create_profile example, **user_data passes the key-value pairs from the user_data dictionary as if they were individual arguments in the function call.
- This technique is incredibly useful when you have data already structured in a dictionary. The function's
**extraparameter simply collects these arguments, allowing you to work with dynamic or pre-configured information without extra steps.
Combining positional arguments, *args, and **kwargs
def flexible_function(required, *args, **kwargs):
print(f"Required parameter: {required}")
print(f"Positional args: {args}")
print(f"Keyword args: {kwargs}")
flexible_function("Hello", 1, 2, 3, name="Python", version=3.9)--OUTPUT--Required parameter: Hello
Positional args: (1, 2, 3)
Keyword args: {'name': 'Python', 'version': 3.9}
Python enforces a specific order when you combine these parameter types in a function definition: standard arguments first, then *args, and finally **kwargs. This structure is what gives the flexible_function its power.
- The first argument,
"Hello", is assigned to the required positional parameter,required. *argsthen collects any remaining positional arguments—in this case,1, 2, 3—into a tuple.**kwargsgathers all keyword arguments, likename="Python"andversion=3.9, into a dictionary.
Advanced kwargs techniques
Building on the basics, you'll now see how **kwargs enables more complex patterns, like forwarding arguments between functions and using them within class methods.
Forwarding kwargs to another function
def format_data(**kwargs):
return {k.upper(): v for k, v in kwargs.items()}
def process_and_format(value, **kwargs):
print(f"Processing value: {value}")
formatted = format_data(**kwargs)
print(f"Formatted data: {formatted}")
process_and_format("test", name="Alice", id=12345)--OUTPUT--Processing value: test
Formatted data: {'NAME': 'Alice', 'ID': 12345}
You can use **kwargs to pass arguments from one function to another, a technique known as forwarding. This pattern is perfect for creating flexible wrapper functions that don't need to know the specifics of the function they're calling.
- The
process_and_formatfunction gathers keyword arguments likename="Alice"into its ownkwargsdictionary. - It then unpacks this dictionary using
**kwargswhen callingformat_data. - This sends the original arguments along, allowing
format_datato process them independently without the wrapper function needing to know what they are.
Using kwargs with default parameters
def configure_app(debug=False, **settings):
config = {"debug": debug, "version": "1.0"}
config.update(settings)
print(f"App configuration: {config}")
configure_app(debug=True, host="localhost", port=8080, timeout=30)--OUTPUT--App configuration: {'debug': True, 'version': '1.0', 'host': 'localhost', 'port': 8080, 'timeout': 30}
You can combine **kwargs with parameters that have default values, like debug=False in the configure_app function. This lets you define core settings while still accepting any number of extra options. It’s a powerful pattern for creating functions with a mix of fixed and flexible arguments.
- The
**settingsparameter gathers all other keyword arguments, such ashostandport, into a dictionary. - Using
config.update(settings), you can then merge these optional settings into a base configuration, making it easy to add new configurations on the fly.
Using **kwargs in class methods and inheritance
class Parent:
def __init__(self, **kwargs):
self.config = kwargs
print(f"Parent initialized with: {self.config}")
class Child(Parent):
def __init__(self, name, **kwargs):
super().__init__(**kwargs) # Pass kwargs to parent
self.name = name
print(f"Child {name} initialized")
child = Child(name="Child1", setting1="value1", setting2="value2")--OUTPUT--Parent initialized with: {'setting1': 'value1', 'setting2': 'value2'}
Child Child1 initialized
In classes, **kwargs is powerful for managing initialization options across an inheritance hierarchy. The Child class’s __init__ method captures its specific name argument, while **kwargs collects any others, like setting1 and setting2.
- The key is the
super().__init__(**kwargs)call, which forwards the extra arguments to theParentclass’s initializer. - This pattern decouples the child from the parent. The
Childclass doesn't need to know what arguments its parent accepts, making the design more flexible and easier to maintain.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. You can describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
The **kwargs techniques from this article are perfect for building flexible applications. With Replit Agent, you can turn these concepts into production tools:
- Build a customizable logging utility that uses
**kwargsto capture optional metadata likeuser_idorsession_token. - Create a flexible component rendering function that passes any number of HTML attributes or style properties using
**kwargs. - Deploy an API client that forwards optional filter and sorting parameters to an endpoint by collecting them with
**kwargs.
Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser.
Common errors and challenges
While **kwargs is powerful, a few common mistakes can trip you up, but they're easy to fix once you know what to look for.
- Forgetting to unpack dictionaries with
**. A frequent mistake is passing a dictionary to a function that expects**kwargswithout unpacking it using the**operator. If you call a function likemy_func(my_dict)instead ofmy_func(**my_dict), Python sees the whole dictionary as a single positional argument, which usually results in aTypeError. - Keyword argument after
**kwargsparameter. Python's function syntax has a strict order. If you try to define a function with a keyword argument after**kwargs, such asdef my_func(**kwargs, another_arg='default'), you'll get aSyntaxErrorbecause the order is invalid. - Using non-string keys with
**kwargs. Keyword argument names must always be strings. If you try to unpack a dictionary that contains non-string keys, like{1: 'one'}, into a function call with**, Python will raise aTypeErrorbecause integer keys aren't valid identifiers for keyword arguments.
Forgetting to unpack dictionaries with **
It's easy to forget the double-asterisk ** when passing a dictionary to a function expecting keyword arguments. Python then treats the dictionary as a single positional argument, not separate keywords, which typically causes an error. The following code shows this mistake in action.
def create_user(username, email, **profile_data):
print(f"User: {username}, Email: {email}")
print(f"Profile data: {profile_data}")
user_profile = {"bio": "Python developer", "location": "Remote"}
create_user("jane_doe", "[email protected]", user_profile) # Wrong!
The create_user function is called with three positional arguments, but it only accepts two. The user_profile dictionary is incorrectly passed as a third argument, causing an error. The following example shows the correct way to call it.
def create_user(username, email, **profile_data):
print(f"User: {username}, Email: {email}")
print(f"Profile data: {profile_data}")
user_profile = {"bio": "Python developer", "location": "Remote"}
create_user("jane_doe", "[email protected]", **user_profile) # Correctly unpacks
The fix is to unpack the dictionary using the double-asterisk operator (**). When you call create_user(**user_profile), you're telling Python to treat the dictionary's contents as individual keyword arguments. This expands user_profile, passing bio="Python developer" and location="Remote" to the function.
The **profile_data parameter then correctly collects these arguments. You'll often encounter this pattern when passing configuration data or API responses that are already stored in a dictionary.
Keyword argument after **kwargs parameter
Python's function definitions follow a strict order, and placing a keyword argument after the **kwargs parameter violates this rule. This mistake will immediately raise a SyntaxError because the interpreter can't parse the invalid signature. The following code shows this error in practice.
def configure_app(**settings, debug=False): # SyntaxError
config = {"debug": debug}
config.update(settings)
print(f"Configuration: {config}")
configure_app(host="localhost", port=8080, debug=True)
The configure_app function defines debug=False after **settings, which isn't valid syntax. Python expects keyword-only arguments to appear before **kwargs in a function signature. The following example shows the correct way to structure the function.
def configure_app(debug=False, **settings): # Named params before **kwargs
config = {"debug": debug}
config.update(settings)
print(f"Configuration: {config}")
configure_app(debug=True, host="localhost", port=8080)
The fix is to define named parameters before **kwargs. By placing debug=False before **settings in the configure_app function, you follow Python's required order. This lets the function correctly assign the debug argument, while **settings collects all other keyword arguments like host and port. Keep this rule in mind when creating functions that mix explicit and arbitrary keyword arguments.
Using non-string keys with **kwargs
Keyword arguments in Python must be strings. When you unpack a dictionary using the ** operator, its keys become these arguments. If your dictionary contains non-string keys, like an integer, Python can't process it and raises a TypeError.
The following code shows what happens when you try to unpack a dictionary containing a non-string key into a function that accepts **kwargs.
data = {
"name": "Product",
"price": 29.99,
123: "identifier" # Non-string key
}
def process_product(**product_data):
for key, value in product_data.items():
print(f"{key}: {value}")
process_product(**data) # TypeError: keywords must be strings
The call to process_product(**data) fails because the dictionary key 123 is an integer. Python cannot convert this number into a valid keyword argument, as all keyword arguments must be strings. The following example shows how to fix this.
data = {
"name": "Product",
"price": 29.99,
"id": 123 # String key with numeric value
}
def process_product(**product_data):
for key, value in product_data.items():
print(f"{key}: {value}")
process_product(**data) # Works correctly
The fix is to ensure all dictionary keys are strings before unpacking them. The ** operator requires string keys to create valid keyword arguments for a function. In the corrected example, the integer key 123 is replaced with the string "id", allowing the process_product(**data) call to work as expected. This issue often arises when you're processing data from external sources like APIs, where keys might not always be strings.
Real-world applications
Now that you can navigate the common pitfalls, you'll see how **kwargs is used to build flexible database queries and API wrappers.
Using **kwargs to build database queries
**kwargs is perfect for building functions that generate dynamic database queries, allowing you to filter results based on any number of search criteria.
def find_users(**query_params):
if not query_params:
return "SELECT * FROM users"
base_query = "SELECT * FROM users WHERE "
conditions = [f"{key} = '{value}'" for key, value in query_params.items()]
final_query = base_query + " AND ".join(conditions)
return final_query
# Generate different queries based on provided parameters
print(find_users())
print(find_users(username="john_doe"))
print(find_users(status="active", role="admin"))
The find_users function uses **query_params to dynamically build SQL queries based on the arguments you provide. This makes your code adaptable to various search needs.
- When called without arguments, it generates a simple query to fetch all users.
- When you pass keyword arguments like
status="active", it uses them to construct aWHEREclause, linking multiple conditions withAND.
This approach is highly efficient. You can filter by any combination of fields without needing to write custom logic for each query, making your data retrieval functions much more reusable.
Creating flexible API wrappers with **kwargs
You can also use **kwargs to build flexible API clients that merge default settings with custom parameters for each request.
class APIClient:
def __init__(self, base_url, **default_params):
self.base_url = base_url
self.default_params = default_params
def get_resource(self, endpoint, **request_params):
# Combine default params with request-specific params
params = {**self.default_params, **request_params}
query_string = "&".join([f"{k}={v}" for k, v in params.items()])
url = f"{self.base_url}/{endpoint}"
if query_string:
url += f"?{query_string}"
return f"GET {url}"
client = APIClient("https://api.example.com", version="v1", api_key="abc123")
print(client.get_resource("users"))
print(client.get_resource("posts", limit=10, sort="date"))
The APIClient class uses **kwargs to manage different layers of configuration. The __init__ method uses **default_params to store reusable settings like version and api_key. When you call get_resource, it uses **request_params for call-specific options.
- The line
params = {**self.default_params, **request_params}is key. It unpacks both dictionaries into a new one, with values fromrequest_paramsoverwriting any defaults if their keys match. - This pattern lets you create a pre-configured client while still allowing custom overrides for individual API calls, like adding
limit=10.
Get started with Replit
Turn your knowledge of **kwargs into a real tool. Tell Replit Agent: "Build a Python function that creates a JSON config file from keyword arguments" or "Create a script that filters a list of dictionaries using keyword arguments."
Replit Agent writes the code, tests for errors, and deploys your app. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.


.png)
.png)