✳️

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

1

All parameters after `*,` must be passed by keyword — TypeError if positional

2

`*` is just a marker, not a parameter — same effect as anything after `*args`

3

The only way to put non-default after default arguments

4

Forces intent at call sites — prevents cryptic positional `True` arguments

5

Ruby's `name:` is keyword-only by definition — no equivalent syntax needed

Use Cases

Functions taking boolean flags — force `enable_xxx=True` syntax Functions with 5+ parameters — block positional calls and require keywords API entry points — guarantee call sites survive signature changes