In this lesson, you’ll learn about how to add positional-only arguments to your functions in Python 3.8.
The built-in function float()
can be used for converting text strings and numbers to float objects. Consider the following example:
>>> float("3.8")
3.8
>>> help(float)
class float(object)
| float(x=0, /)
|
| Convert a string or number to a floating point number, if possible.
[...]
Look closely at the signature of float()
. Notice the slash (/
) after the parameter. What does it mean?
It turns out that while the one parameter of float()
is called x
, you’re not allowed to use its name:
>>> float(x="3.8")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: float() takes no keyword arguments
When using float()
, you’re only allowed to specify arguments by position, not by keyword. Before Python 3.8, such positional-only arguments were only possible for built-in functions. There was no easy way to specify that arguments should be positional-only in your own functions:
>>> def incr(x):
... return x + 1
...
>>> incr(3.8)
4.8
>>> incr(x=3.8)
4.8
In Python 3.8, you can use /
to denote that all arguments before it must be specified by position. You can rewrite incr()
to only accept positional arguments:
>>> def incr(x, /):
... return x + 1
...
>>> incr(3.8)
4.8
>>> incr(x=3.8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: incr() got some positional-only arguments passed as
keyword arguments: 'x'
By adding /
after x
, you specify that x
is a positional-only argument. You can combine regular arguments with positional-only ones by placing the regular arguments after the slash:
>>> def greet(name, /, greeting="Hello"):
... return f"{greeting}, {name}"
...
>>> greet("Christopher")
'Hello, Christopher'
>>> greet("Christopher", greeting="Awesome job")
'Awesome job, Christopher'
>>> greet(name="Christopher", greeting="Did it work?")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
greet(name="Christopher", greeting="Did it work?")
TypeError: greet() got some positional-only arguments passed as
keyword arguments: 'name'
In greet()
, the slash is placed between name
and greeting
. This means that name
is a positional-only argument, while greeting
is a regular argument that can be passed either by position or by keyword.
At first glance, positional-only arguments can seem a bit limiting and contrary to Python’s mantra about the importance of readability. You may find that there are not a lot of occasions where positional-only arguments improve your code.
However, in the right circumstances, positional-only arguments can give you some flexibility when you’re designing functions. First, positional-only arguments make sense when you have arguments that have a natural order but are hard to give good, descriptive names to.
Another possible benefit of using positional-only arguments is that you can more easily refactor your functions. In particular, you can change the name of your parameters without worrying that other code depends on those names.
Positional-only arguments nicely complement keyword-only arguments. In any version of Python 3, you can specify keyword-only arguments using the star (*)
. Any argument after *
must be specified using a keyword:
>>> def to_fahrenheit(*, celsius):
... return 32 + celsius * 9 / 5
...
>>> to_fahrenheit(40)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: to_fahrenheit() takes 0 positional arguments but 1 was given
>>> to_fahrenheit(celsius=40)
104.0
celsius
is a keyword-only argument, so Python raises an error if you try to specify it based on position, without the keyword.
You can combine positional-only, regular, and keyword-only arguments by specifying them in this order separated by /
and *
. In the following example, text
is a positional-only argument, border
is a regular argument with a default value, and width
is a keyword-only argument with a default value:
>>> def headline(text, /, border="~", *, width=50):
... return f" {text} ".center(width, border)
...
Since text
is positional-only, you can’t use the keyword text
:
>>> headline("Positional-only Arguments")
'~~~~~~~~~~~ Positional-only Arguments ~~~~~~~~~~~~'
>>> headline(text="This doesn't work!")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: headline() got some positional-only arguments passed as
keyword arguments: 'text'
border
, on the other hand, can be specified both with and without the keyword:
>>> headline("Python 3.8", "=")
'=================== Python 3.8 ==================='
>>> headline("Real Python", border=":")
':::::::::::::::::: Real Python :::::::::::::::::::'
Finally, width
must be specified using the keyword:
>>> headline("Python", "@", width=38)
'@@@@@@@@@@@@@@@ Python @@@@@@@@@@@@@@@'
>>> headline("Python", "@", 38)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
headline("Python", "@", 38)
TypeError: headline() takes from 1 to 2 positional arguments
but 3 were given
For more details on this topic, check out the following resources:
- PEP 457 provides an in-depth discussion on the
/
notation. - PEP 570 gives more details on Python positional-only parameters.
- What Are Python Asterisk and Slash Special Parameters For? is a deep dive into the
*
and/
parameters.
Ihor Udod on Jan. 9, 2020
How do you achieve this highlighting in VS Code terminal?