What Slash (/) And Asterisk (*) Are In A Function Definition — Python
Python Special Parameters
Not a member yet? You can still this for free here
Have you ever seen a Python code that looks like this?
def function(name, age, *, job, salary):
...Or one that looks like this:
def function(name, age, /, job, salary):
...I saw the first one I believe one year ago or more, and it was so intriguing for me that I had to find out more about it and what is its purpose.
The second one, on the other hand, I only recently came across while reading things about Python3.12.
No, it’s not a feature new to Python 3.12.
By the end of this article, you will learn the following:
- What does slash mean in function definition
- What does the asterisk mean in a function definition
Understanding these two concepts can help you improve your code, improve performance, and make your code easier to maintain over time.
Without further ado, let’s get into them.
Slash and asterisk as operators
As you probably know, slash — / and asterisk — * are operators in Python. They are used to perform mathematical operations — division and multiplication respectively.
def divide_by_n[T: (int, float)](n: T) -> T:
return n / n # divide n by n
def multiply_by_n[T: (int, float)](n: T) -> T:
return n * nOur functions receive a generic type that either be an int or float and returns the same type as the one received.
Asterisk (*) In Function Definition
There are three ways to use the asterisk in a function definition. a) We use it to represent a tuple — args — of arguments b) to represent a dictionary — kwargs — of arguments and c) to define a function that receives only keyword arguments.
def func(*args, **kwargs): # represents a) and b)
print(args, kwargs)
print(func(1, 2, first_name="Jane", last_name="Doe"))
# (1, 2), {'first_name': 'Jane', 'last_name': 'Doe'}But what we are interested in, is the option c). Using an asterisk to define keyword-only arguments.
But first, let’s define keyword arguments to ensure we are all on the same page.
By keyword arguments, we mean calling a function and sending arguments by the parameters’ name.
def func(first_name: str, last_name: str):
print(f'Hello, {first_name} {last_name}')Our function func has two parameters — first_name and last_name. So calling by sending arguments by keyword, means that we explicitly specify which value we are assigning to which parameter:
func(last_name='Doe', first_name='Jane')
# Hello, Jane DoeAs you can see, using keyword arguments we can send values in a different order from the way we defined our function with.
But since our function accepts both keyword arguments and positional arguments (we will define in the next section), if we call our function using positional arguments we might run into problems:
func('Doe', 'Jane')
# Hello, Doe JaneThis is not the output we expected from our function.
So how can we avoid situations like this when we want to make sure arguments are sent properly?
The solution here is to define our function in a way that it only accepts keyword arguments.
That’s where an asterisk in the function definition can be very handy as well.
def func(*, first_name: str, last_name: str): # asterisk in parameters
print(f'Hello, {first_name} {last_name}')By defining our function with an asterisk in the parameters definition, we are saying that everything that comes after it — on the right side — are keyword arguments only.
func('Jane', last_name='Doe') ❌
# TypeError: func() takes 0 positional arguments
# but 1 positional argument (and 1 keyword-only argument) were given
func(first_name='Jane', last_name='Doe') ✅
# Hello, Jane DoeSo any attempt to call func with positional arguments in this case results in a TypeError.
— Mixing positional and keyword arguments
Using an asterisk in the function definition means that everything after it will be keyword only.
This means that everything before it can be either positional or keyword.
def func(message: str, *, first_name: str, last_name: str):
print(f'{message}, {first_name} {last_name}')
func('Welcome back', first_name='Jane', last_name='Doe') # positional message
# Welcome back, Jane Doe
func(message='Hi', first_name='Jane', last_name='Doe') # keyword message
# Hi, Jane DoeSlash (/) In Function Definition
Back in the day, Python only supported positional arguments by default. It was not possible to choose whether you call a function and use the parameter(s) name or send by position:
def divide_by_n[T: (int, float)](n: T) -> T:
return n / n # divide n by n
divide_by_n(10) # call and send argument by positionIt was only in Python 1.0 that it became possible to call a function and use the parameters’ name or position:
divide_by_n(n=10) # call using name in argumentsIf you’re wondering where T in function divide_by_n comes from, it is a new feature from Python 3.12 to create generic types. You can read more about it here
Introduced in PEP 570, slash gives the ability to say that our function only accepts positional arguments.
# using slash in a function definition
def divide_by_n[T: (int, float)](n: T, /) -> T:
return n / n # divide n by nThis means that everything on the left side of our slash can only be positional arguments. It’s the opposite of the asterisk that we saw earlier.
“how about using ‘/’ ? It’s kind of the opposite of ‘*’ which means “keyword argument”, and ‘/’ is not a new character.” — Guido van Rossum, PEP 570
def func(first_name: str, last_name: str, /):
print(f'Welcome, {first_name} {last_name}')In this case, we are explicitly saying that our function should be called with positional arguments only. So the user calling it should be aware of the outcome in case the arguments are sent in the wrong position.
func('Jane', 'Doe') ✅
# f'Welcome, Jane Doe
func('Jane', last_name='Doe') ❌
# TypeError: func() got some positional-only arguments passed as keyword arguments: 'last_name'— Why should we restrict to positional arguments only?
One of the questions you might ask is why we should stick to positional arguments only.
- Optimization
There’s a method called convention name METH_FASTCALL, which is responsible for supporting positional arguments.
This method, according to PEP 570, has been specialized for a function that accepts positional-only arguments. This improvement means a reduction of the cost that Python has to handle empty keywords.
One of the reasons we see so many built-in functions accepting positional-only arguments (like int, float, and str), is because of the performance improvement they can give.
- Maintainability
Using positional arguments gives us the flexibility to change the name of our arguments without the fear of breaking things.
def func(f_name: str, l_name: str, /):
print(f'Welcome, {f_name} {l_name}')Like in this example, where we previously had as parameters first_name and last_name.
Changing the parameters’ name does not affect the users’ code when interacting with our function.
func('Jane', 'Doe')
# f'Welcome, Jane DoeMore good reasons on why and when to use it can be found in this well-documented PEP.
Final Thoughts
One of the things we love about Python is the flexibility to write our code and mix things. The ability to focus on getting things done without worrying too much about things that ‘do not matter’.
Knowing things like these can improve the way we code and make us a better Python programmer.
When deciding to use one of these, understand what you want to accomplish and how your code will evolve.
Resources used in this article:
Hi! Thank you for your time reading my article. If you enjoyed this article and would like to receive similar content directly to your inbox
In Plain English 🚀
Thank you for being a part of the In Plain English community! Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: X | LinkedIn | YouTube | Discord | Newsletter
- Visit our other platforms: Stackademic | CoFeed | Venture | Cubed
- More content at PlainEnglish.io





