Loading video player…

Specifying Positional-Only Arguments

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:

Python
>>> 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:

Python
>>> 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:

Python
>>> 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:

Python
>>> 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:

Python
>>> 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:

Python
>>> 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:

Python
>>> def headline(text, /, border="~", *, width=50):
...     return f" {text} ".center(width, border)
...

Since text is positional-only, you can’t use the keyword text:

Python
>>> 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:

Python
>>> headline("Python 3.8", "=")
'=================== Python 3.8 ==================='

>>> headline("Real Python", border=":")
':::::::::::::::::: Real Python :::::::::::::::::::'

Finally, width must be specified using the keyword:

Python
>>> 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:

00:00 This video is going to talk about the new feature of positional-only arguments. I’ll have you start with an example. I want to start this video out by talking about a built-in function named float().

00:09 float() can be used to convert text strings or numbers to float objects, like this. If you bring up the help() function on the built-in function float, if you look at the signature for float right here, you’ll notice that there’s a slash (/) after the parameter. What does it mean? Well, it turns out that there’s one parameter of float() and it’s called x, and you’re not allowed to use the name of the parameter when calling it. When using float(), you’re allowed to specify arguments only by position, not by a keyword. Let me show you what I mean. So again, notice the slash (/) here. Pressing q to get out of help(). So if you try float() again and say x="3.8"—a text string—you’ll see here that it says float() takes no keyword arguments, only this positional argument.

00:53 You can’t use a keyword when you specify the argument. Before Python 3.8, there was no easy way to specify that arguments should be positional-only in your own functions. So if you were to define something like this function—we’ll call it incr() (increment).

01:10 When you call incr(), and let’s say you give it a float, you could use either method, and that’s normal for a regular argument. And that’s where the change comes in in Python 3.8.

01:24 You could rewrite the increment incr() function to accept positional-only arguments. It would look more like this, with a comma (,) then a slash (/), and the rest is the same.

01:40 So when you call incr(), that works fine. But if you were to now use the keyword, something new happens. It says here incr() got some positional-only arguments passed as keyword arguments.

01:55 So that will raise an exception and give you a TypeError. You can use this almost as a divider between your positional-only arguments and regular arguments. Let me have you try that out with another example.

02:07 Define a new function named greet(). greet() is going to take a name as a positional-only argument, then you’re going to put that slash (/) in there so that you can combine positional-only with regular arguments. So here’s a regular argument of a greeting.

02:23 Here you’re going to return an f-string with the greeting. If you haven’t worked with f-strings yet, I’ll include a link in the text below to articles and courses on it.

02:31 Comma, space, the name. So when you call greet()—again, the default greeting is going to be "Hello".

02:37 You’re going to enter in the name here as a positional-only.

02:43 Great. You could also call greet()

02:49 and give it a greeting.

02:55 But what you can’t do—or what will give you an error—is if you type name,

03:08 here you’ll get an exception of a TypeError. Again, just like on the previous one for incr(), greet() got some positional-only arguments passed as keyword arguments, specifically name. At first glance, positional arguments can seem a bit limiting and contrary to Python’s mantra about the importance of readability.

03:26 You’ll probably find that there are not a lot of occasions where positional-only arguments improve your code. However, in the right circumstances, it can give you some flexibility when you’re designing functions.

03:35 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 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.

03:57 To repeat that information, how do you specify positional-only arguments? You use the slash (/) to denote that arguments before it must be specified by position. To learn more about the slash-notation, PEP 457 provides an in-depth discussion about it. PEP 570 dives much deeper into Python positional-only parameters. Links to both are in the text below this video.

04:20 The complement to positional-only arguments would be keyword-only arguments, and those are available in any version of Python 3. You can specify that you want keyword-only arguments by using an asterisk (*).

04:31 Any arguments after that * must be specified by using a keyword. Here is an example converting to Fahrenheit, and you can see here that it has a keyword-only argument of celsius.

04:46 Let me have you try that function out. So here, to specify keyword-only, you’d put an * and then put the keyword argument after—in this case, celsius. And in this case, return

05:02 32 + celsius * 9 / 5. So here, if you were to call to_fahrenheit() and just give it a value, say 40 degrees Celsius, it says here that it takes 0 positional arguments but 1 was given.

05:16 So it required that you have a keyword there in order to call it properly. And also, depending on your circumstances, you may want to combine positional-only, regular, and keyword-only arguments. To do that, you would specify them separated by that slash (/) and the asterisk (*).

05:35 So it would look something like this. Here, a positional-only of text, the slash (/). Here, the standard argument.

05:46 And now, after the border, using the *, the keyword-only argument of width, which has a default value of 50.

05:54 You’re returning an f-string with a space in front of the value for the text argument and then another space. And then you’re using the text string method of .center() with two parameters that you’re taking from those arguments, width and border.

06:08 The only one that’s positional is text. You could start with headline() and say "Positional-only".

06:18 Here, you can see the default value that’s going to be that type of border with a width of 50, looking something like that. Again, since it’s positional-only, if you were to give it text,

06:35 that would raise an error, a TypeError. For border, it’s a regular argument. So in this case, you could say just a value, say like, the equal sign ("=") instead.

06:49 Or you could actually specify that border equals, say, a colon (:). It will accept either style. But the width must be specified using a keyword if you’re going to enter it in…

07:07 overriding the default with a value of 38.

07:15 If you were to give it just the value of 38, that again would raise an exception, getting a TypeError: headline() takes 1 to 2 positional arguments but 3 were given. So again, that one needs to be a keyword argument.

07:29 So as you can see, positional-only arguments can nicely complement regular arguments and keyword-only arguments. In the next video, you’ll learn how Python 3.8 enhances type checking by using more precise types.

Avatar image for Ihor Udod

Ihor Udod on Jan. 9, 2020

How do you achieve this highlighting in VS Code terminal?

Avatar image for Chris Bailey

Chris Bailey RP Team on Jan. 10, 2020

Hi @Ihor Udod, I’m using the REPL replacement named BPython, I have links and more information about it in the previous lesson, below the video.

Avatar image for Ihor Udod

Ihor Udod on Jan. 10, 2020

Thank you Chris!

Avatar image for agbehiogiri

agbehiogiri on March 30, 2020

Hello Chris, bpython seems not to work on a Windows platform.

Avatar image for Denis Roy

Denis Roy on March 30, 2020

@agbehiogiri: it is reported that bpython can work on Windows but only after jumping through a few hoops. See the documentation for the details.

Avatar image for Chris Bailey

Chris Bailey RP Team on March 31, 2020

Hi @agbehiogiri and @Denis Roy, Denis is correct that it requires some hoop jumping to make it work on Windows.

bPython was created under Linux originally, and making it work with macOS wasn’t that different. The issue revolves around bPython using/depending on “Curses”. The consensus after some searching seems to be that you can use this package, windows-curses. You would need to install it via pip, python3 -m pip install windows-curses.

You will also not be able to type just bpython in your terminal application. You would instead use python3 -m bpython.cli. I was able to get this to work on a windows 10 installation yesterday. It warns that the Command Line Interface will be deprecated at some point, so not sure what that means as far as long term support. The other method mentioned in some posts is running bpython by typing bpython-curses.exe. I could not get that to work.

One additional issue you may have is something to do with saving the history, and it crashing on first time running. Here is a link to a stack overflow thread with a solution if that happens to you. It is the first answer with Set-Content -Path...

Overall after doing the research, I think maybe IPython is a much easier solution. Very similar, and has it’s own benefits.

I hope this helps.

Avatar image for djlondon

djlondon on April 26, 2020

Struggling to see a real use case of this. The examples shown highlight how it could be used, but I’d need a really stellar reason to use this to overcome the readability hit. It’s rather cryptic, reminding me of C++!

Avatar image for djlondon

djlondon on April 26, 2020

Update prior to my original comment

This section of PEP 570 is does a pretty good job at explaining desirable use cases.

Avatar image for Dan Bader

Dan Bader RP Team on May 29, 2020

Regarding alternative REPLs, if bpython is difficult to install I can also recommend ptpython. Chris’s recommendation of IPython is also great!

Avatar image for widowmakerreborn

widowmakerreborn on July 7, 2020

most useless feature. The idea of having a name of argument is exactly to reflect calling side intention. I don’t see how why people want to shoot in their leg with this feature.

Avatar image for widowmakerreborn

widowmakerreborn on July 7, 2020

Another problem with position-only arguments is a type of exception -TypeError, why not a ValueError?

Avatar image for varelaautumn

varelaautumn on Sept. 25, 2020

Their justification for the existence of this feature doesn’t seem that bad, but their implementation just looks horrible.

At least the keyword only argument style sort of fits with what you may expect a * to do in Python You’re like, oh okay, so that variable took unpacked all the positional arguments, I guess any argument after the star could only possibly be reached with a keyword. Makes sense.

def meow(*args, name):

If you know anything about *args, you know a positional argument can’t reach name. If you get rid of args, and just type *, you still can see how it’d make sense that would require name have a keyword.

But this ‘/’? How would you have any intuition about what “def meow(name, /)” did?

Avatar image for Arjun Sammi

Arjun Sammi on Oct. 11, 2020

I still do not understand what it means to “specify arguments by position”. Can anyone explain this part to me (the positional-argument thing), Thank you!

Avatar image for Geir Arne Hjelle

Geir Arne Hjelle RP Team on Oct. 12, 2020

@Arjun

Specifying arguments by position means that you rely on the order of arguments for the function to match them.

Consider the following example:

def hello(name, country):
    return f"Hi {name} from {country}"

You can call this function, specifying arguments by position, as follows:

>>> hello("Guido", "Netherlands")
'Hi Guido from Netherlands'

Python matches the arguments by position. name is set to "Guido" because they are both in the first position, and so on. If you change the order, the function will not give the desired result:

>>> hello("Netherlands", "Guido")
'Hi Netherlands from Guido'

In contrast, you can specify arguments by name. In this case, the order of the arguments do not matter:

>>> hello(country="Netherlands", name="Guido")
'Hi Guido from Netherlands'
Avatar image for Arjun Sammi

Arjun Sammi on Oct. 14, 2020

@Geir Thank you for the explanation, I understand it now.

Become a Member to join the conversation.