What Are Python Asterisk and Slash Special Parameters For?

What Are Python Asterisk and Slash Special Parameters For?

by Ian Eyre intermediate python

Whenever you think of Python’s asterisk operator (*), you most likely think of multiplication or exponentiation. Similarly, you probably associate the forward slash operator (/) with division. But you can also use the bare asterisk and slash as special parameters in function headers. These do something completely unrelated to mathematics.

When defining functions, you’ll often include a list of comma-separated parameters to define what types of arguments the user can pass to your function. Using the asterisk and forward slash symbols as special parameters in your function header may look strange at first:

Python
def strange_function(*, x, y):
    ...

def another_strange_function(a, b, /, c, *, d):
    ...

Both of these function definitions may look a bit odd, but their function parameters are actually perfectly valid. So, what exactly do a bare asterisk and slash mean in a Python function definition?

In Short: The Python Asterisk and Slash Control How to Pass Values to Functions

The asterisk (*) and forward slash (/) define whether you can pass positional or keyword arguments to your functions.

You use a bare asterisk to define a boundary between arguments that you can pass by either position or keyword from those that you must pass by keyword. You use the slash to define a boundary between those arguments that you must pass by position from those that you can pass by either position or keyword. Here’s a visual that summarizes how to use these symbols:

Left side Divider Right side
Positional-only arguments / Positional or keyword arguments
Positional or keyword arguments * Keyword-only arguments

You can also use both symbols together. You’ll learn more about this last point later, but for now take a look at the uses of both symbols individually.

Suppose you write an asterisk_usage() function with an asterisk as one of its parameters. Then you call it:

Python
>>> def asterisk_usage(either, *, keyword_only):
...     print(either, keyword_only)
...
>>> asterisk_usage(either="Frank", keyword_only="Dean")
Frank Dean

>>> asterisk_usage("Frank", keyword_only="Dean")
Frank Dean

>>> asterisk_usage("Frank", "Dean")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: asterisk_usage() takes 1 positional argument but 2 were given

You define your function’s either parameter before the asterisk, meaning that you can pass arguments to it by keyword or position. Your function’s keyword_only parameter can accept arguments by keyword only because you defined it after the asterisk. Your first two function calls succeed because you pass the second argument by keyword. Notice that passing the first argument by keyword or position works just fine.

Your third function call fails because you attempted to pass the second argument by position. The error message tells you that you’ve used one too many positional arguments.

Now suppose you write a slash_usage() function with a slash as part of its header. Try it out:

Python
>>> def slash_usage(positional_only, /, either):
...     print(positional_only, either)
...
>>> slash_usage("Frank", either="Dean")
Frank Dean

>>> slash_usage(positional_only="Frank", either="Dean")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: slash_usage() got some positional-only arguments
passed as keyword arguments: 'positional_only'

>>> slash_usage("Frank", "Dean")
Frank Dean

This time, you used the slash to ensure that you must pass any preceding arguments by position. When you pass in positional_only by position, your call works. However, when you pass the same argument by keyword, your call fails. Your final function call shows that you can pass the either argument by either position or by keyword.

Now you know how to use the asterisk and slash when defining your own function. But perhaps you have some questions about what kinds of functions these enable you to write, and maybe you’re also thinking about the asterisk in *args. Keep reading for answers!

Can You Write a Function That Accepts Only Keyword Arguments?

Earlier, you learned that you can’t include the asterisk as the final parameter when defining your function. So you might be surprised to learn that it can be your first parameter. In that case, you’re specifying that your function will only accept keyword arguments.

In Python, a keyword argument is one that you pass to a function using its parameter name—for example, first_name="Dean". Passing your arguments by keyword makes your code more readable and also makes using the function more meaningful to your users. You can pass keyword arguments in any order, but they must come after positional arguments, if applicable.

You already know that parameters defined after the asterisk only take keyword arguments. To write a function that only accepts keyword arguments, you define the asterisk special parameter first:

Python
>>> def print_three_members(*, member1, member2, member3):
...     print(f"member1 is {member1}")
...     print(f"member2 is {member2}")
...     print(f"member3 is {member3}")
...
>>> print_three_members(member1="Frank", member2="Dean", member3="Sammy")
member1 is Frank
member2 is Dean
member3 is Sammy

>>> print_three_members(member1="Frank", member3="Dean", member2="Sammy")
member1 is Frank
member2 is Sammy
member3 is Dean

>>> print_three_members("Frank", "Dean", "Sammy")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print_three_members() takes 0 positional arguments but 3 were given

>>> print_three_members("Frank", member3="Dean", member2="Sammy")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print_three_members() takes 0 positional arguments
but 1 positional argument (and 2 keyword-only arguments) were given

Your print_three_members() function accepts only keyword arguments. Your first two calls successfully pass all the arguments by keyword. Each of your final two calls results in a TypeError because you’ve passed arguments by position. Placing the asterisk as the first parameter means that you must provide all arguments as keyword arguments. The error messages tell you where you went wrong.

Can You Write a Function That Accepts Only Positional Arguments?

In Python, any argument that’s not passed by keyword is passed by position. You must pass positional arguments in the same order that you defined their parameters in the function header. You always pass positional arguments before keyword arguments. Several of Python’s built-in functions require that you pass all the arguments by position. In Python 3.8 and later, you can also enforce this in your own functions.

Earlier, you saw how you must pass any arguments for parameters defined before the slash by position. To write a function that accepts only positional arguments, you define the slash special parameter last:

Python
>>> def print_three_members(member1, member2, member3, /):
...     print(f"member1 is {member1}")
...     print(f"member2 is {member2}")
...     print(f"member3 is {member3}")

>>> print_three_members("Frank", "Dean", "Sammy")
member1 is Frank
member2 is Dean
member3 is Sammy

>>> print_three_members(member1="Frank", member2="Sammy", member3="Dean")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print_three_members() got some positional-only arguments
passed as keyword arguments: 'member1, member2, member3'

>>> print_three_members("Frank", "Dean", member3="Sammy")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print_three_members() got some positional-only arguments
passed as keyword arguments: 'member3'

This time, you can only ever pass member1, member2, and member3 by position. The first call meets this criterion and works, while the others fail because you attempted to use keyword arguments. Again, the error messages keep you on the right track.

Both the bare asterisk and *args let you know which arguments you must pass by keyword. The asterisk means the same in both usages. The args component simply provides a tuple to store any additional positional arguments that you pass.

Before reading further, you might like to review the meaning of the *args function parameter. Suppose you write a get_average() function as shown below:

Python
>>> def get_average(*args):
...     print(args)
...     return sum(args) / len(args)
...

The *args parameter allows you to write a function that can accept a varying number of individual positional arguments in each call. Your code packs all of these arguments into a tuple named args.

Suppose you call your get_average() function twice, first with three arguments and then with five:

Python
>>> get_average(1, 2, 3)
(1, 2, 3)
2.0

>>> get_average(1, 3, 5, 7, 9)
(1, 3, 5, 7, 9)
5.0

When you first call get_average() with three arguments, you can see that the args tuple contains three elements. It contains five after your second call. As long as you pass in one or more integers or floats as arguments, your function will calculate the average. Try it with one or more numerical values, and notice that the function will always work.

As before, if you attempt to mix the two types before *args, the positional arguments must come before the keyword arguments. If you try to add additional positional arguments after some keyword arguments, then *args won’t be able to store those.

Suppose you decide to experiment with your print_three_members() function by replacing the asterisk with *args and renaming it to print_varying_members():

Python
>>> def print_varying_members(member1, member2, *args, member3):
...     print(f"member1 is {member1}")
...     print(f"member2 is {member2}")
...     print(f"member3 is {member3}")
...     print(f"*args contains {args}")

In this example, you can pass arguments corresponding to the parameters member1 and member2 by position, and any excess gets packed into the args tuple. You can only ever pass arguments for member3, however, by keyword:

Python
>>> print_varying_members("Frank", member2="Dean", member3="Sammy")
member1 is Frank
member2 is Dean
member3 is Sammy
*args contains ()

This works because although the parameters member1 and member2 have a mixture of positional and keyword arguments, you’ve passed the positional arguments first. The args tuple is empty because you didn’t pass any additional arguments beyond the defined parameters. Passing everything by keyword also works as before:

Python
>>> print_varying_members(member1="Frank", member2="Dean", member3="Sammy")
member1 is Frank
member2 is Dean
member3 is Sammy
*args contains ()

This time, you can assign the first two keyword arguments. You’ve met the mandatory keyword argument requirement for member3. For the same reason as before, the args tuple is still empty. If, however, you deviate slightly, things go very wrong:

Python
>>> print_varying_members(member1="Frank", "Dean", member3="Sammy")
  File "<stdin>", line 1
    print_varying_members(member1="Frank", "Dean", member3="Sammy")
                                                                ^
SyntaxError: positional argument follows keyword argument

This code fails because it breaks one of the golden rules. You tried to pass an argument by position after passing another by keyword. Again, the error message explains your error.

The main point of *args is to absorb any additional positional arguments that you pass for which there are no defined parameters. However, if you want to pass additional arguments, then you must make sure to pass everything before and including *args by position as well. You can’t pass some by keyword and then others by position, because your positional arguments must always come before your keyword arguments, as you’ll now see:

Python
>>> print_varying_members("Frank", "Dean", "Peter", "Joey", member3="Sammy")
member1 is Frank
member2 is Dean
member3 is Sammy
*args contains ('Peter', 'Joey')

This time, you’ve passed four positional arguments, with the mandatory member3 keyword argument also present. Your code has safely packed two additional positional arguments into the args tuple. Try replacing the fourth positional argument with a named argument like member4="Joey". The *args parameter can’t store this additional named argument anywhere, so your code will crash.

Can You Use the Asterisk Without Other Parameters?

The asterisk is designed to force you to pass all subsequent arguments by keyword. If you use it alone, then there are no subsequent parameters to accept these arguments. As you can see below, you won’t get past the function definition line:

Python
>>> def print_three_members(*):
  File "<stdin>", line 1
    def print_three_members(*):
                            ^
SyntaxError: named arguments must follow bare *

If you attempt to write a function with only a bare asterisk, then all you’ll succeed in doing is producing a SyntaxError. Forget it!

The same isn’t true of *args. You can use this alone, as you saw earlier with the get_average(*args) function. The args tuple can accept as many positional arguments as you wish. Naturally, you can’t pass arguments by keyword because there aren’t any named parameters defined in the function.

Can You Use Both the Asterisk and Slash in a Function Definiton?

As you know, the asterisk forces you to pass all subsequent arguments by keyword only, while the slash forces you to pass all previous arguments by position. When you use both together, the slash must come before the asterisk.

If you switched the order, then the asterisk would require keyword-only arguments between the two symbols, while the slash would demand positional-only arguments in that space. You can pass arguments to any parameters between the slash and asterisk by either position or keyword.

To see this in action, suppose you write a function named print_four_members() that includes both the slash and asterisk in the correct order:

Python
>>> def print_four_members(member1, member2, /, member3, *, member4):
...     print(f"member1 is {member1}")
...     print(f"member2 is {member2}")
...     print(f"member3 is {member3}")
...     print(f"member4 is {member4}")
...

In the above example, you must pass the arguments for member1 and member2 by position because of the slash. You can pass the member3 argument by either keyword or position, while you must pass member4 by keyword. Next you perform different test cases:

Python
>>> print_four_members("Frank", "Dean", member3="Sammy", member4="Joey")
member1 is Frank
member2 is Dean
member3 is Sammy
member4 is Joey

As you can see, everything works as expected. Try passing the "Sammy" argument by position for yourself, and you’ll get the same result.

Now try defining the slash and asterisk the other way around. You’ll produce a SyntaxError:

Python
>>> def print_four_members(member1, member2, *, member3, /, member4):
  File "<stdin>", line 1
    def print_four_members(member1, member2, *, member3, /, member4):
                                                         ^
SyntaxError: / must be ahead of *

Your function definition is illegal here because the asterisk forces you to pass all subsequent parameters by keyword, while the / forces you to pass previous parameters by position. There’s confusion about how you intend to pass member3. Python doesn’t know either, so it gives up! As the error message reminds you, the slash must go before the asterisk.

By placing both special operators together in the correct order, you can force anything to the left of the slash to be passable by position only and anything to the right of the asterisk to be passable by keyword only. Nothing else is possible:

Python
>>> def print_three_members(member1, member2, /, *, member3):
...     print(f"member1 is {member1}")
...     print(f"member2 is {member2}")
...     print(f"member3 is {member3}")
...
>>> print_three_members("Frank", "Dean", member3="Sammy")
member1 is Frank
member2 is Dean
member3 is Sammy

>>> print_three_members("Frank", "Dean", "Sammy")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print_three_members() takes 2 positional arguments but 3 were given

>>> print_three_members("Frank", member2="Dean", member3="Sammy")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: print_three_members() got some positional-only arguments
passed as keyword arguments: 'member2'

When you experiment with the code above, you must pass the first two arguments by position and the third by keyword. As you can see, trying it any other way simply doesn’t work.

As you might expect, mixing them around once again triggers a SyntaxError:

Python
>>> def print_three_members(member1, member2, *, /, member3):
  File "<stdin>", line 1
    def print_three_members(member1, member2, *, /, member3):
                                                 ^
SyntaxError: / must be ahead of *

Why not try out some other combinations to consolidate your understanding of these rules?

Can You Use Both *args and the Asterisk Together?

Recall that the *args parameter and bare asterisk parameter both force you to pass all subsequent arguments by keyword. If you try to use *args either before or after the bare asterisk, then a SyntaxError occurs:

Python
>>> def print_three_members(member1, member2, *args, *, member3):
  File "<stdin>", line 1
    def print_three_members(member1, member2, *args, *, member3):
                                                     ^
SyntaxError: * argument may appear only once

>>> def print_three_members(member1, member2, *, *args, member3):
  File "<stdin>", line 1
    def print_three_members(member1, member2, *, *args, member3):
                                                 ^
SyntaxError: * argument may appear only once

The syntax error occurs because Python is unhappy at having to tell you to pass all subsequent arguments by keyword twice. The error message reminds you to use a single asterisk once.

Why Would You Use the Special Parameters in Your Function?

There are several reasons why you might need to do this: to compensate for poorly named parameters, to ensure backward compatibility, and to provide internal documentation.

One common benefit of forcing positional arguments occurs when your function’s parameter names don’t clearly indicate their purpose. Revealing these names to your users to allow keyword passing would introduce unreadable code and confuse the user.

Suppose you write a simple function to produce a username, but you force it to accept only positional arguments:

Python
>>> def username(fn, ln, /):
...     return ln + fn[0]
...
>>> print(username("Frank", "Sinatra"))
SinatraF

>>> print(username(fn="Frank", ln="Sinatra"))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: username() got some positional-only arguments
passed as keyword arguments: 'fn, ln'

To make sure your username() function accepts arguments by position only, you use the slash as the final parameter. As you can see, if you try and pass arguments by their keywords, then the call fails. The function call itself isn’t very intuitive because of your poorly named parameters.

But even though your function has two poorly named parameters, these are always hidden from the user. As a Real Python reader who knows that readability counts, you’d probably take the opportunity to update them, but this may involve lots of updates within the function body as well. This can, in itself, lead to problems. Clever use of the slash has avoided the need for this.

A second common use case is backward compatibility. Suppose that lots of code uses your original username() function, but you decide to enhance it. In addition to the vague parameter names, you now have the problem of ensuring the new version is backward compatible with the earlier one. Suppose you update your function as follows:

Python
>>> def username(fn, ln, /, *, initial_last=True):
...     if initial_last:
...         return ln + fn[0]
...     else:
...         return fn[0] + ln
...

The updated version of username() still needs two positional arguments, but it provides the option of using the initial_last keyword argument. Luckily, initial_last is a sensible name, so users won’t misunderstand. Because initial_last has a default value, this parameter is optional. However, if you don’t want to use the default value, False, then you must pass your argument to it by keyword.

You first decide to test the function to make sure it’s indeed backward compatible:

Python
>>> username("Frank", "Sinatra")
SinatraF

With relief, you see that the answer is identical to the one that you produced earlier. Motivated, you next decide to test the new functionality:

Python
>>> username("Frank", "Sinatra", initial_last=False)
FSinatra

As a sample test, you decide to pass initial_last=False by keyword. Again, both results look fine.

Finally, you try to run the function by passing in all three arguments by keyword and position:

Python
>>> username(fn="Frank", ln="Sinatra", initial_last=False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: username() got some positional-only arguments
passed as keyword arguments: 'fn, ln'

This time, the code crashes. This is good because you didn’t want your users to use your function in this way anyway.

One other benefit of using the asterisk and slash is that it adds to your function’s internal documentation. By including the asterisk and slash, you make it clear to anyone reading your function, as well as yourself if you forget, how you intend arguments to be passed to it. It lets you know which arguments are keyword-only, positional-only, or either.

Conclusion

In this tutorial, you’ve learned that the forward slash (/) and asterisk (*) mean more than division and multiplication. The slash special parameter allows you to force certain arguments by position, while the bare asterisk forces some arguments by keyword. When you’re using both symbols together, the slash must come first, and any parameters defined in between can take positional or keyword arguments.

You also saw how these symbols are useful if you need to create functions that you can update while still retaining backward compatibility, and you learned how *args relates to the bare asterisk.

What’s your number-one takeaway or favorite tidbit that you’ve learned? How are you going to put your newfound skills to use? Leave a comment below and let the community know.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Ian Eyre

Ian is an avid Pythonista and Real Python contributor who loves to learn and teach others.

» More about Ian

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!