Skip to content

Latest commit

 

History

History
52 lines (40 loc) · 1.95 KB

File metadata and controls

52 lines (40 loc) · 1.95 KB

Strictly Separate Positional And Keyword Arguments

Typically when I define a function with arguments in Python, I can choose pass the arguments to that function as positional or keyword arguments. I can even mix and match as long as all positional arguments come before all keyword arguments.

But what if I want to enforce a strict boundary between what arguments are positional and what arguments are keyword?

By combining the positional-only marker (/) and keyword-only marker (*), I can get exactly that effect.

Here I have defined a connect function that takes host and port arguments. Because both appear before the / marker, they must be passed as positional. I also have a timeout argument with a default. Because timeout comes after the * marker, it must be passed as a keyword argument.

def connect(host, port, /, *, timeout=30):
    print(f"Connecting to #{host}:#{port}")
    print(f"  Timeout: {timeout}s")

    # ...

Let's see it in action.

>>> connect("localhost", 3000, timeout=20)
Connecting to #localhost:#3000
  Timeout: 20s

>>> connect(host="localhost", port=4000)
Traceback (most recent call last):
  File "/Users/lastword/dev/misc/python-experiments/arguments.py", line 37, in <module>
    connect(host="localhost", port=4000)
TypeError: connect() got some positional-only arguments passed as keyword arguments: 'host, port'

This second attempt of calling connect with host and port as keyword arguments presents as a runtime TypeError because they were expected as "positional-only arguments".

In my editor that line also presents with two static typing errors call-arg: Unexpected keyword argument "port" for "connect" (and the equivalent for host).

Note: I did some additional reading just now in Python in a Nutshell, 4th Edition and I see that it is more appropriate to call them Named Arguments instead of Keyword Arguments.