Marking End of Positional Parameters in Python

A bare * in a Python function signature marks the end of positional parameters. Everything after it is keyword-only: callers must pass it by name.

def exec(self, command: str, *, workdir: str | None = None, tty: bool = False): 
	...

exec("ls", workdir="/tmp", tty=True)  # OK
exec("ls", "/tmp")                     # TypeError

The Aha!: the * doesn’t change how valid calls execute. It changes which calls are valid. It’s a constraint on the caller, not on the runtime.

Where is it helpful?

  • Signatures are contracts as much as mechanics. The * costs nothing at runtime but encodes intent: “these are tuning knobs, name them.”
  • It buys the API author freedom. Keyword-only params can be reordered or added anywhere without breaking callers, which matters most in base classes and interfaces shared by several implementations.
  • It kills a class of silent bugs: exec("ls", None, 10.0, True) is unreadable and easy to get wrong. Bare booleans and numbers at call sites carry no meaning.
  • It also shows up in introspection (inspect.signature() reports KEYWORD_ONLY), which matters for tools that build CLIs or schemas from function signatures.