What is **kwargs in Python? Complete guide

Discover **kwargs in Python: definition, practical examples and best practices for using variable keyword arguments in your functions.
9 min read
Believemy logo

Definition of **kwargs in Python

In Python, **kwargs is a special parameter used in a function definition to accept a variable number of keyword arguments. The term "kwargs" is an abbreviation of "keyword arguments". Concretely, when you write **kwargs in a function's signature, Python collects all the additional keyword arguments passed to that function and stores them in a dict (dictionary).

This mechanism is extremely powerful: it allows you to create flexible functions capable of receiving an undetermined number of parameters without having to declare them explicitly. It is a fundamental concept that you will find in many Python libraries and that we explore in depth in our complete Python course.

Good to know

The double asterisk ** is the key operator. The name "kwargs" is simply a convention: you could just as well write **options or **parameters. It is the ** that tells Python to capture keyword arguments.


How does **kwargs work?

When a function is called with keyword arguments that do not correspond to explicitly defined parameters, Python groups them into a dictionary. The argument names become the dictionary keys, and the passed values become the associated values.

Here is a first simple example:

PYTHON
def display_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

display_info(name="Alice", age=30, city="Paris")

This code outputs:

PYTHON
name: Alice
age: 30
city: Paris

As you can see, the three keyword arguments name, age and city were automatically collected into the kwargs dictionary. You can then iterate over this dictionary with the .items() method to access key-value pairs.


Difference between *args and **kwargs

It is essential to clearly distinguish *args and **kwargs. These two mechanisms are complementary but work differently:

Characteristic*args**kwargs
Container typetupledict
Arguments acceptedPositional (unnamed)Keyword (named)
Call syntaxfunc(1, 2, 3)func(a=1, b=2)
Accessing valuesBy indexBy key

Here is an example that combines both:

PYTHON
def complete_function(*args, **kwargs):
    print("Positional arguments:", args)
    print("Keyword arguments:", kwargs)

complete_function(1, 2, 3, name="Alice", age=30)

Result:

PYTHON
Positional arguments: (1, 2, 3)
Keyword arguments: {'name': 'Alice', 'age': 30}
Warning

The order of parameters in a function signature is important: regular parameters come first, then *args, then **kwargs. Reversing this order will cause a syntax error.


Practical examples of using **kwargs

Creating a flexible configuration function

One of the most common use cases for **kwargs is creating configuration functions. You can define default values while allowing the user to override them:

PYTHON
def configure_application(**kwargs):
    config = {
        "debug": False,
        "port": 8080,
        "host": "localhost",
        "timeout": 30
    }
    config.update(kwargs)
    return config

# Usage with custom parameters
my_config = configure_application(debug=True, port=3000)
print(my_config)
# {'debug': True, 'port': 3000, 'host': 'localhost', 'timeout': 30}

In this example, the function defines a default configuration dictionary, then uses update() to replace the values specified by the user. This is a very common design pattern in the Python ecosystem.


Passing arguments to another function

Another very common use of **kwargs is passing arguments to an underlying function. This is a pattern you will often encounter in decorators and wrapper functions:

PYTHON
def log_call(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"Result: {result}")
        return result
    return wrapper

@log_call
def calculate_price(base_price, tax=0.2, discount=0):
    return base_price * (1 + tax) - discount

calculate_price(100, tax=0.1, discount=5)

Result:

PYTHON
Calling calculate_price with kwargs: {'tax': 0.1, 'discount': 5}
Result: 105.0

Here, the log_call decorator uses **kwargs in its wrapper function to intercept and forward all keyword arguments without needing to know them in advance.


Building classes with variable options

You can use **kwargs in the __init__ method of a class to allow flexible initialization:

PYTHON
class Widget:
    def __init__(self, name, **kwargs):
        self.name = name
        self.width = kwargs.get("width", 100)
        self.height = kwargs.get("height", 50)
        self.color = kwargs.get("color", "white")
        self.visible = kwargs.get("visible", True)
    
    def __repr__(self):
        return (f"Widget(name='{self.name}', width={self.width}, "
                f"height={self.height}, color='{self.color}')")

# Creation with different options
button = Widget("MyButton", color="blue", width=200)
print(button)
# Widget(name='MyButton', width=200, height=50, color='blue')

The dictionary's .get() method is particularly useful here, as it allows you to define a default value if the key does not exist in kwargs.


Dictionary unpacking with **

The ** operator is not only used to capture arguments. It can also unpack a dictionary to pass it as keyword arguments to a function:

PYTHON
def create_profile(name, age, city, profession):
    return f"{name}, {age} years old, {city} - {profession}"

info = {
    "name": "Alice",
    "age": 30,
    "city": "Paris",
    "profession": "Developer"
}

# Unpacking the dictionary
profile = create_profile(**info)
print(profile)
# Alice, 30 years old, Paris - Developer

This technique is extremely practical when you already have your data structured in a dictionary. The ** operator "unpacks" each key-value pair from the dictionary into the corresponding keyword argument.


Merging dictionaries with **

Since Python 3.5, you can use ** to merge multiple dictionaries elegantly:

PYTHON
defaults = {"theme": "light", "language": "en", "notifications": True}
user_preferences = {"theme": "dark", "font_size": 14}

# Merging with ** (Python 3.5+)
final_config = {**defaults, **user_preferences}
print(final_config)
# {'theme': 'dark', 'language': 'en', 'notifications': True, 'font_size': 14}

# Alternative with | (Python 3.9+)
final_config_v2 = defaults | user_preferences
print(final_config_v2)
# {'theme': 'dark', 'language': 'en', 'notifications': True, 'font_size': 14}

Note that when keys are identical, the last dictionary wins. Here, "theme" is "dark" because user_preferences is unpacked second.


Combining regular parameters, *args and **kwargs

When you combine different types of parameters, you must follow a strict order in your function def signature:

PYTHON
def universal_function(required_param, default_param="value", *args, **kwargs):
    print(f"Required: {required_param}")
    print(f"With default: {default_param}")
    print(f"Extra args: {args}")
    print(f"Extra kwargs: {kwargs}")

universal_function("hello", "world", 1, 2, 3, debug=True, verbose=False)

Result:

PYTHON
Required: hello
With default: world
Extra args: (1, 2, 3)
Extra kwargs: {'debug': True, 'verbose': False}

The order to follow is always the following:

  1. Required positional parameters
  2. Parameters with default values
  3. *args — variable positional arguments
  4. **kwargs — variable keyword arguments
Warning

**kwargs must always be the last parameter in the signature. Placing anything after **kwargs will cause a SyntaxError.


Best practices with **kwargs

1. Validate the received keys

A drawback of **kwargs is that it accepts any keyword argument, including those with typos. Remember to validate the keys:

PYTHON
def configure(**kwargs):
    valid_keys = {"debug", "port", "host", "timeout"}
    unknown_keys = set(kwargs.keys()) - valid_keys
    
    if unknown_keys:
        raise TypeError(f"Unknown arguments: {unknown_keys}")
    
    return kwargs

# This will raise an error
try:
    configure(debuq=True)  # Typo!
except TypeError as e:
    print(e)  # Unknown arguments: {'debuq'}


2. Prefer explicit parameters when possible

Do not use **kwargs as a shortcut to avoid declaring parameters. If you know the expected parameters, declare them explicitly for better readability and better autocompletion support:

PYTHON
# ❌ Less readable
def create_user(**kwargs):
    name = kwargs.get("name")
    email = kwargs.get("email")
    age = kwargs.get("age", 0)

# ✅ More explicit and readable
def create_user(name, email, age=0):
    pass


3. Document the expected kwargs

When you use **kwargs, add a detailed docstring to indicate the accepted keyword arguments:

PYTHON
def send_email(recipient, subject, **kwargs):
    """Sends an email.
    
    Args:
        recipient: Recipient's email address.
        subject: Email subject.
        **kwargs: Additional options:
            - cc (str): Carbon copy address.
            - bcc (str): Blind carbon copy address.
            - priority (int): Priority level (1-5).
            - html (bool): Send as HTML (default: False).
    """
    cc = kwargs.get("cc")
    bcc = kwargs.get("bcc")
    priority = kwargs.get("priority", 3)
    html = kwargs.get("html", False)
    # ... sending logic


4. Use kwargs for inheritance and delegation

In class hierarchies, **kwargs is ideal for passing arguments to the parent constructor without listing them all:

PYTHON
class Animal:
    def __init__(self, name, age, **kwargs):
        self.name = name
        self.age = age
        self.attributes = kwargs

class Dog(Animal):
    def __init__(self, breed, **kwargs):
        super().__init__(**kwargs)
        self.breed = breed

my_dog = Dog(breed="Labrador", name="Rex", age=5, color="golden")
print(my_dog.name)        # Rex
print(my_dog.breed)       # Labrador
print(my_dog.attributes)  # {'color': 'golden'}


Advanced use cases

Kwargs with type annotations

Since Python 3.12, you can use TypedDict and Unpack to precisely type your **kwargs:

PYTHON
from typing import TypedDict, Unpack

class ConfigOptions(TypedDict, total=False):
    debug: bool
    port: int
    host: str

def start_server(**kwargs: Unpack[ConfigOptions]) -> None:
    debug = kwargs.get("debug", False)
    port = kwargs.get("port", 8080)
    host = kwargs.get("host", "localhost")
    print(f"Server on {host}:{port} (debug={debug})")

start_server(debug=True, port=3000)

This approach offers the best of both worlds: the flexibility of **kwargs with the safety of static typing.


Kwargs in lambda functions

lambda functions can also use **kwargs, although this remains uncommon:

PYTHON
# Lambda with **kwargs
display = lambda **kwargs: print(", ".join(f"{k}={v}" for k, v in kwargs.items()))
display(x=1, y=2, z=3)
# x=1, y=2, z=3


Common mistakes to avoid

Here are the most frequent mistakes when working with **kwargs:

PYTHON
# ❌ Mistake 1: Forgetting ** when unpacking
def greet(name, city):
    print(f"Hello {name} from {city}")

info = {"name": "Alice", "city": "Paris"}
# greet(info)       # TypeError!
greet(**info)        # ✅ Correct

# ❌ Mistake 2: Non-string dictionary keys
# **kwargs only accepts string keys
bad_dict = {1: "one", 2: "two"}
# def test(**kwargs): pass
# test(**bad_dict)  # TypeError!

# ❌ Mistake 3: Duplicate argument
def say_hello(name, **kwargs):
    pass
# say_hello("Alice", name="Bob")  # TypeError: multiple values for argument 'name'
Warning

The keys of a dictionary unpacked with ** must be strings. Any other key type will cause a TypeError.


Frequently asked questions

Question

What is the difference between *args and **kwargs?

*args collects additional positional arguments into a tuple, while **kwargs collects additional keyword arguments into a dict. For example, in func(1, 2, name="Alice"), the values 1 and 2 would go into args, and name="Alice" would go into kwargs. Both can be used in the same function, but *args must always precede **kwargs.


Question

Is the name "kwargs" mandatory?

No, kwargs is simply a convention commonly adopted by the Python community. You can use any valid variable name after the double asterisk **. For example, **options, **parameters or **config will work perfectly fine. However, we strongly recommend following the **kwargs convention so that your code is immediately understandable by other developers.


Question

Can you modify kwargs inside a function?

Yes, kwargs is a regular Python dictionary. You can freely add, modify or delete keys. However, these modifications will only be visible within the function's scope, they will not affect the caller's variables. This is identical behavior to any dict passed as an argument.


Question

How can I learn to master **kwargs and Python functions?

To properly master **kwargs, it is essential to practice with concrete examples: decorators, classes with inheritance, configuration functions. We recommend following our dedicated Python course on Believemy, which covers in detail functions, variable arguments, decorators and many other advanced concepts with practical exercises.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.