How Python Keyword Arguments Work
python keyword arguments: Learn how Python keyword arguments work, including keyword-only and positional-only parameters, **kwargs, ordering rules, and API design guid...
Python keyword arguments let you pass values to a function by naming the parameter each value should fill, rather than relying on position. When you write connect(host="db.example.com", port=5432), each value is bound to the parameter with the matching name, regardless of the order in which the arguments appear. This makes call sites more readable and removes a class of bugs caused by swapped positional values.
What Keyword Arguments Are in Python
A function call in Python can pass values by position or by name. Positional arguments are matched to parameters by their order in the call. Keyword arguments are matched by the parameter name written at the call site.
def connect(host, port, timeout): print(host, port, timeout) # Positional arguments connect("db.example.com", 5432, 30) # Keyword arguments connect(host="db.example.com", port=5432, timeout=30)
Both calls reach the same function body. The difference is how the caller expresses the mapping between values and parameters. Keyword arguments make the intent of each value explicit, which matters when a function takes several parameters of the same type, such as two integers or two strings.
How Keyword Arguments Differ From Positional Arguments
Positional arguments rely on the order in which parameters are declared. The first value goes to the first parameter, the second to the second, and so on. This is compact but error-prone when a function has many parameters.
Keyword arguments remove the ordering requirement. The caller can pass them in any order:
connect(timeout=30, host="db.example.com", port=5432)
This is equivalent to the earlier calls. The caller does not need to remember the parameter order, only the parameter names. That eliminates a class of bugs where values are swapped between parameters of the same type.
Keyword arguments also make the call site self-documenting. A call like connect("db.example.com", 5432, 30) requires the reader to know the parameter order. A call like connect(host="db.example.com", port=5432, timeout=30) states what each value means without requiring the reader to look up the function signature.
Keyword-Only Arguments and the * Separator
Python allows a function to require that certain parameters be passed only as keyword arguments. Place a bare * in the parameter list. Every parameter declared after the * becomes keyword-only.
def configure(endpoint, *, retries=3, verify_tls=True): print(endpoint, retries, verify_tls) # Valid configure("https://api.example.com", retries=5) # Invalid: retries is keyword-only configure("https://api.example.com", 5)
The second call raises a TypeError because retries cannot be supplied positionally. The * separator forces the caller to name the parameter, which prevents accidental misordering of optional configuration values.
This pattern is common in library APIs where optional parameters control behavior. Requiring keyword syntax for those parameters keeps the positional signature small and stable, so later additions of optional parameters do not break existing callers.
Positional-Only Arguments and the / Separator
Python 3.8 introduced the / separator for positional-only parameters. Parameters declared before the / cannot be passed as keyword arguments.
def divide(numerator, denominator, /, *, precision=6): return round(numerator / denominator, precision) # Valid divide(10, 3) # Invalid: numerator is positional-only divide(numerator=10, denominator=3)
Positional-only parameters are useful when parameter names are not part of the public API contract. A library author can rename those parameters without breaking callers who use keyword syntax. This is the same reasoning that led the standard library to mark many parameters as positional-only in recent versions.
The / and * separators can appear together in one signature. Parameters before / are positional-only, parameters between / and * can be passed either way, and parameters after * are keyword-only.
Using **kwargs for Variable Keyword Arguments
The **kwargs parameter collects any keyword arguments that do not match a declared parameter into a dictionary. This is useful for forwarding options to another function or for accepting an open-ended set of configuration values.
def make_request(url, **kwargs): headers = kwargs.get("headers", {}) timeout = kwargs.get("timeout", 10) print(url, headers, timeout) make_request("https://api.example.com", headers={"Authorization": "Bearer x"}, timeout=15)
The **kwargs dictionary maps each keyword name to its value. The function decides which keys it cares about and ignores the rest, or forwards them elsewhere.
A common use is passing options through a wrapper:
def retry_call(func, *args, **kwargs): for attempt in range(3): try: return func(*args, **kwargs) except Exception: if attempt == 2: raise
The wrapper accepts any positional arguments and any keyword arguments and forwards them unchanged to the wrapped function. This keeps the wrapper generic without declaring every parameter of every function it might call.
Parameter Ordering Rules and Common Errors
Python enforces a specific order in a function definition. The full signature layout is:
- Positional-or-keyword parameters
- Positional-only parameters (after
/) - Keyword-only parameters (after
*) **kwargs
A common error is placing a parameter with a default value before one without a default. Python requires that non-default parameters come before default parameters in the positional section:
# Invalid def f(a=1, b): pass
This raises a SyntaxError because b has no default but follows a parameter that does. The fix is to reorder so that b comes first.
Another common error is mixing keyword and positional arguments incorrectly at the call site. Once a keyword argument appears, all following arguments must also be keyword arguments:
# Invalid connect("db.example.com", port=5432, 30) # Valid connect("db.example.com", port=5432, timeout=30)
The first call raises a SyntaxError because 30 follows a keyword argument without a name.
Performance and Runtime Considerations
Keyword arguments have a small runtime cost compared to positional arguments. When Python executes a call with keyword arguments, it must build a mapping of names to values and match those names against the function's parameter list. Positional arguments are matched by index, which avoids that name lookup.
The difference is measurable only in hot loops with millions of calls. For normal application code, the readability benefit of keyword arguments outweighs the microsecond-level cost. If profiling shows that a function called millions of times is a bottleneck, converting its keyword arguments to positional arguments is one possible optimization, but it should be verified with a profiler rather than applied speculatively.
The **kwargs mechanism has a higher cost than named keyword arguments because it builds a dictionary for every call. A function that uses **kwargs in a tight loop will allocate a new dictionary on each invocation. If the function only needs a few known options, declaring them as named parameters avoids that allocation.
Designing APIs With Keyword Arguments
When you design a function or method that other developers will call, keyword arguments give you control over how the API is used. The * separator forces callers to name optional parameters, which prevents them from relying on positional order that may change.
A reasonable guideline is to keep the first few parameters positional-or-keyword when they are the core inputs that every caller must provide, and to mark optional configuration parameters as keyword-only with the * separator. Use / when parameter names should not be part of the public contract. Avoid **kwargs when the set of accepted options is small and known, because named parameters provide better documentation and editor autocompletion.
The standard library uses these patterns extensively. Functions like sorted accept key and reverse as keyword-only arguments, which keeps the positional signature minimal while allowing optional behavior to be expressed clearly. Following the same pattern in your own APIs makes them easier to use correctly and harder to misuse.