Positional-only and keyword-only parameters (/ and *)
The gotcha
def f(a, *, b) and def f(a, /, b) look similar but mean opposite things. * makes everything after keyword-only; / makes everything before positional-only. Mixing them up changes which calls are valid.
Minimal repro
def keyword_only(a, *, b):
return a, b
keyword_only(1, 2) # TypeError: takes 1 positional arg but 2 were given
keyword_only(1, b=2) #
keyword_only(a=1, b=2) #
def positional_only(a, /, b):
return a, b
positional_only(1, 2) #
positional_only(1, b=2) #
positional_only(a=1, b=2) # TypeError: 'a' is positional-only
def both(a, /, b, *, c):
return a, b, c
both(1, 2, c=3) #
both(1, b=2, c=3) #
both(a=1, b=2, c=3) # TypeError: 'a' is positional-only
both(1, 2, 3) # TypeError: 'c' is keyword-only
The three regions
def f(positional_only, /, normal, *, keyword_only):
pass
| Position | What’s allowed |
|---|---|
Before / |
positional-only — caller cannot use name=value |
Between / and * |
positional-or-keyword (the default) — either works |
After * |
keyword-only — caller must use name=value |
Both / and * are markers, not real parameters. Either or both can be omitted.
Why keyword-only (*)
Force callers to use named arguments for clarity:
def fetch(url, *, timeout=10, retries=3, follow_redirects=True):
...
fetch("https://...", 30, 5, False) # unreadable
fetch("https://...", timeout=30, retries=5, follow_redirects=False) #
This is the more common of the two. Use * for any function with multiple boolean / number flags — readability at the call site dominates.
A bare * (no *args) is the marker:
def f(a, b, *, c, d): ... # c, d are keyword-only; no var args
def f(a, b, *args, c, d): ... # c, d keyword-only after *args
Why positional-only (/)
Three reasons, in order of importance:
- API stability — change a parameter’s name without breaking callers.
- Match C built-ins —
len(obj),min(a, b),dict(other)— these are positional-only because they always have been;len(obj=x)is a TypeError. - Avoid
**kwargsclashes — when**kwargsis part of your signature,/lets you name a parameter the same as a kwarg key without conflict.
def merge(default, /, **overrides):
# 'default' could collide with overrides if it were positional-or-keyword
return {**default, **overrides}
merge({"a": 1}, default={"b": 2}) # — 'default' kwarg goes into overrides
Real-world examples
min / max are positional-only (mostly):
min(1, 2, 3) #
min(iterable=[1, 2, 3]) # TypeError — `iterable` is positional-only
# But min() does accept a `key` kwarg:
min([1, -2, 3], key=abs) # — positional-only / keyword-only mix
dict.update:
{"a": 1}.update(other={"b": 2}) # treats 'other' as a key, not a parameter name
# Implementation: def update(self, /, *args, **kwargs)
functools.partial:
import functools
def divide(num, denom): return num / denom
double = functools.partial(divide, denom=2)
double(num=10) #
If divide were def divide(num, /, denom), the denom=2 partial application would be safe (no risk of caller passing num=... and conflicting).
Keyword-only with default
The most common real use:
def request(url, *, method="GET", timeout=10, headers=None):
...
The first parameter is positional (concise call), the rest must be named. Pretty much every modern Python library uses this pattern for its main API.
When to add either
| Situation | Use |
|---|---|
| Function has 3+ parameters | * keyword-only for everything past the obvious main one |
| Boolean flags | always keyword-only — func(x, True, False) is unreadable |
| Two parameters that would be confused at the call site | keyword-only |
| Library API where you want freedom to rename | positional-only for the main arg |
**kwargs overlaps with a real parameter name |
positional-only for the parameter |
For application code, you’ll use * (keyword-only) constantly and / (positional-only) rarely.
Version notes
- Keyword-only
*— Python 3.0+ (PEP 3102). - Positional-only
/— Python 3.8+ (PEP 570). Before 3.8 it was achievable only via C extensions or weird**kwargsintrospection. inspect.signatureshows both correctly.
Interview angle
- Q: “What’s the difference between
def f(a, *, b)anddef f(a, /, b)?” —*makes b keyword-only;/makes a positional-only. - Q: “When would you use
*to mark keyword-only?” — readability for boolean flags / multi-arg functions; force callers to name arguments. - Follow-up: “When would you use
/?” — API stability (rename without breaking callers), match C built-ins, avoid name clashes with**kwargs. - Follow-up: “What does
def f(a, b, /, c, *, d)mean?” —a, bpositional-only;cpositional-or-keyword;dkeyword-only.