Back to Blog
Python

Python args vs kwargs: Choosing the Right Function Parameters

python args vs kwargs: Learn the difference between *args and **kwargs in Python, when to use each, and how they affect function design and maintainability.

pythonargskwargsfunction parametersvariable arguments
Illustration comparing Python *args and **kwargs function parameters

When you define a Python function, you often know exactly how many arguments it will accept. But there are cases where the number of arguments is unknown at design time. Python handles this with *args and **kwargs, two syntax forms that collect extra positional and keyword arguments. Understanding python args vs kwargs is essential for writing flexible functions, decorators, and APIs that accept variable input.

What Are *args and **kwargs?

In Python, *args and **kwargs are not reserved keywords but conventional names for two special syntax forms. When you place an asterisk before a parameter name in a function definition, Python collects any extra positional arguments into a tuple. Two asterisks before a parameter name collect extra keyword arguments into a dictionary. The names args and kwargs are conventions; you can use any name after the asterisk, but these are widely recognized.

The key distinction is that *args handles positional arguments, while **kwargs handles keyword arguments. This is the core of python args vs kwargs.

Syntax and Basic Usage

Consider a function that accepts any number of positional arguments:

def collect(*args): print(args) collect(1, 2, 3) # (1, 2, 3)

Similarly, a function that accepts any number of keyword arguments:

def collect_keywords(**kwargs): print(kwargs) collect_keywords(a=1, b=2) # {'a': 1, 'b': 2}

You can combine both with normal parameters. The order must be: normal parameters, *args, keyword-only parameters, **kwargs. For example:

def f(a, b, *args, c=10, **kwargs): print(a, b, args, c, kwargs)

Here, a and b are required, extra positional arguments go to args, c is a keyword-only argument with a default, and any additional keyword arguments go to kwargs.

Aspect*args**kwargs
CollectsPositional argumentsKeyword arguments
Typetupledict
Syntax in definitiondef f(*args)def f(**kwargs)
Unpacking syntaxf(*iterable)f(**mapping)

When to Use *args

Use *args when a function needs to accept a variable number of positional arguments without knowing the exact count at definition time. A common use case is a sum function:

def total(*numbers): return sum(numbers) print(total(1, 2, 3)) # 6

Another frequent use is in decorators. A decorator wraps a function and may need to forward any arguments the wrapped function receives. Using *args and **kwargs in the wrapper ensures it works with any signature:

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

Here, *args and **kwargs are used both to collect and to unpack when calling the original function.

When to Use **kwargs

Use **kwargs when a function needs to accept arbitrary keyword arguments. This is common in configuration systems, where you might want to set many options without defining each one as a parameter. For example:

def configure(**options): for key, value in options.items(): setattr(config, key, value)

**kwargs also appears in frameworks that pass options through to other functions. For instance, a wrapper that forwards extra keyword arguments to a library call.

Common Mistakes and Pitfalls

A frequent mistake is confusing the collecting syntax with the unpacking syntax. In a function definition, *args collects extra arguments. In a function call, *args unpacks an iterable into positional arguments. The same applies to **kwargs for dictionaries.

def f(a, b, c): print(a, b, c) args = (1, 2, 3) f(*args) # Unpacks tuple into positional arguments kwargs = {'a': 1, 'b': 2, 'c': 3} f(**kwargs) # Unpacks dict into keyword arguments

Another pitfall is overusing *args and **kwargs when explicit parameters would make the function clearer. If a function only ever receives two or three specific arguments, defining them explicitly improves readability and enables static analysis. Reserve *args and **kwargs for cases where the number of arguments is genuinely variable or when you need to forward arguments transparently.

Performance and Maintainability Considerations

*args and **kwargs introduce a small runtime overhead because Python must create a tuple and a dictionary to hold the collected arguments. For most functions this is negligible, but in tight loops or extremely hot paths, it can matter. If performance is critical, prefer explicit parameters where the argument count is fixed.

Maintainability is a more significant concern. Functions with *args and **kwargs are harder to inspect because their signatures do not reveal what arguments they accept. This makes debugging and documentation more difficult. Use them deliberately, and consider adding type hints or docstrings to clarify expected inputs.

Advanced Patterns and Limitations

Python 3 supports keyword-only arguments by placing a bare * in the parameter list. For example:

def f(a, *, b): return a + b

Here, b must be passed as a keyword argument. This is useful when you want to enforce clarity in calls.

When overriding methods in subclasses, you can use *args and **kwargs to accept any arguments the parent method might pass, but this can hide signature mismatches. Prefer explicit signatures when the interface is fixed.

Another limitation is that *args and **kwargs cannot be used with certain built-in functions that rely on exact signatures, but that is rare. In general, they are a powerful tool for writing flexible and reusable code, but they should not replace well-defined parameters when the API is known.

python args vs kwargs: Practical Usage and Code Examples | RYUSLOG DEV