Python Keyword-Only Arguments Using Asterisk
This article explains how Python uses the asterisk (*)
symbol in function definitions to enforce keyword-only arguments. You
will learn the syntax for implementing a bare asterisk versus
*args, how Python's runtime enforces these constraints by
raising specific TypeError exceptions, and the practical
software design benefits of requiring explicit parameter names during
function calls.
The Role of the Asterisk
(*)
In Python, any parameter defined after an asterisk in a function signature cannot be supplied as a positional argument. The asterisk acts as a boundary marker that consumes positional arguments, requiring callers to explicitly name any following parameters.
There are two ways to define keyword-only arguments:
- A bare asterisk (
*): Used when the function does not accept arbitrary positional arguments. - A variable positional parameter
(
*args): Used when the function accepts arbitrary positional arguments, with subsequent parameters strictly bound to keyword calls.
Syntax and Implementation
1. Using a Bare Asterisk
A standalone * does not create a parameter itself;
instead, it signals the end of positional arguments.
def configure_server(host, port, *, secure=False, timeout=30):
return f"Connecting to {host}:{port} (Secure: {secure}, Timeout: {timeout})"In this signature:
hostandportcan be passed positionally or by keyword.secureandtimeoutmust be specified by name.
# Valid calls:
configure_server("localhost", 8080)
configure_server("localhost", 8080, secure=True)
# Invalid call:
configure_server("localhost", 8080, True, 60)2. Using *args
If a function takes an arbitrary number of positional arguments via
*args, any parameter placed after *args
automatically becomes keyword-only.
def calculate_sum(*values, round_output=False):
total = sum(values)
return round(total) if round_output else total
# Valid calls:
calculate_sum(1.2, 2.5, 3.1)
calculate_sum(1.2, 2.5, 3.1, round_output=True)
# Invalid call:
calculate_sum(1.2, 2.5, 3.1, True)In the invalid call, True is treated as another value
collected by *values, rather than being assigned to
round_output.
How Python Enforces Keyword-Only Constraints
Python enforces keyword-only rules at runtime during parameter binding. When a function is invoked, the Python interpreter maps provided values to parameters in three steps:
- It binds provided positional arguments to parameters appearing
before the
*or*args. - Any excess positional arguments are either absorbed by
*argsor trigger an error if a bare*was used. - It binds keyword arguments to matching parameter names.
If you attempt to pass a keyword-only argument positionally when
using a bare *, Python raises a TypeError:
configure_server("localhost", 8080, True)
# TypeError: configure_server() takes 2 positional arguments but 3 were givenIf a keyword-only argument does not have a default value and is omitted from the call, Python raises a missing argument error:
def create_user(username, *, email):
pass
create_user("johndoe")
# TypeError: create_user() missing 1 required keyword-only argument: 'email'Why Use Keyword-Only Arguments?
- Eliminates the "Boolean Trap": Functions accepting
flags like
process(data, True, False)become difficult to read. Enforcing keywords makes calls explicit:process(data, validate=True, notify=False). - Protects API Evolution: You can safely add new configuration parameters to existing functions without worrying that users' positional arguments will unintentionally map to the wrong variables.
- Prevents Ordering Mistakes: When functions accept multiple parameters of the same data type, forcing keyword usage prevents silent bugs caused by swapping the order of arguments.