Keyword-only Arguments — What `*,` Means and Why You Use It
That single asterisk in `def f(a, b, *, c)`
Syntax
def foo(a, b, *, c):
...
foo(1, 2, c=3) # OK
foo(1, 2, 3) # TypeError
foo(c=3, a=1, b=2) # OK
The * itself is a marker, not a real parameter. It means "everything after this is keyword-only."
Relation to *args
*args has the same effect — anything after it becomes keyword-only automatically.
def bar(a, *args, c): # c is keyword-only
...
Think of bare * as *args when you don't actually need the args.
Why Use It
1. Place non-default arguments after default ones
def f(a=1, b=2, enable_evidence: bool): # SyntaxError
...
def f(a=1, b=2, *, enable_evidence: bool): # OK
...
Non-default after default is illegal positionally, but fine as keyword-only.
2. Self-documenting call sites
process(item, filter_list, True)
# What's True? enabled? force? dry_run?
process(item, filter_list, enable_evidence=True)
# Obvious
3. Safer signature changes
Reorder parameters and existing call sites still work.
Standard Library Examples
sorted(iterable, *, key=None, reverse=False)
print(*objects, sep=' ', end='\n')
That's why sorted(list, lambda x: x) fails and you have to write sorted(list, key=lambda x: x).
Ruby Comparison
Ruby has no *, syntax. It doesn't need one.
def foo(a, b, c:)
# ...
end
foo(1, 2, c: 3) # OK
foo(1, 2, 3) # ArgumentError
Ruby's c: is keyword-only from the start. Positional and keyword arguments are syntactically separated, unlike Python where the same parameter can be passed either way by default.
| Python | Ruby | |
|---|---|---|
| Default param | Both positional and keyword | Positional only |
| Keyword-only | After *, |
Declared as name: |
| Var positional | *args |
*args |
| Var keyword | **kwargs |
**kwargs |
Ruby's stance: "if you want it as keyword, declare it as keyword." Python's stance: "the same parameter can be constrained on how it's called."
Key Points
All parameters after `*,` must be passed by keyword — TypeError if positional
`*` is just a marker, not a parameter — same effect as anything after `*args`
The only way to put non-default after default arguments
Forces intent at call sites — prevents cryptic positional `True` arguments
Ruby's `name:` is keyword-only by definition — no equivalent syntax needed