What Do / and * Mean in Python Function Parameters?
1. Purpose
In this post, I will demonstrate what the / and * symbols mean in Python function signatures, and how to correctly call functions that use these special parameter markers.
I first encountered this syntax when reading Python documentation:
def func(a, b, /, c, d, *, e, f): passMy immediate reaction was confusion. What are those / and * symbols doing in the middle of a parameter list?
2. The Problem and Solution
2.1 What Error I Got
I tried calling a function with keyword arguments for what I thought were normal parameters:
def greet(name, /, greeting="Hello"): print(f"{greeting}, {name}!")
greet(name="Alice") # I thought this would workInstead of a friendly greeting, I got a TypeError:
TypeError: greet() got some positional-only arguments passed as keyword arguments: 'name'This error confused me. Python normally allows both positional and keyword arguments, so why was this failing?
2.2 Understanding the Parameter Zones
After digging into the Python documentation, I learned that / and * create distinct “zones” in the parameter list:
[positional-only] / [positional-or-keyword] * [keyword-only] a, b c, d e, fThe five parameter types in Python are:
- Positional-only (before
/): Must be passed by position, cannot use keyword syntax - Positional-or-keyword (between
/and*): Can use either style - Keyword-only (after
*): Must be passed by keyword, cannot use positional syntax - Var positional (
*args): Collects extra positional arguments - Var keyword (
**kwargs): Collects extra keyword arguments
2.3 The Correct Usage
Going back to my greet function, the name parameter is before the /, so it’s positional-only:
def greet(name, /, greeting="Hello"): print(f"{greeting}, {name}!")
# Correct ways to call:greet("Alice") # OK: positional-only by positiongreet("Alice", "Hi") # OK: greeting can be positionalgreet("Alice", greeting="Hi") # OK: greeting can be keyword
# Wrong way:greet(name="Alice") # TypeError: positional-only as keyword2.4 Keyword-Only Parameters
The * symbol works similarly but in reverse. Parameters after * must be keyword-only:
def create_user(name, *, age, email): return {"name": name, "age": age, "email": email}
# Correct:
# Wrong:The error message I got:
TypeError: create_user() takes 1 positional argument but 3 positional arguments were given2.5 Full Example with All Zones
Here’s a function demonstrating all parameter zones:
def complex_func(a, b, /, c, d, *, e, f): """ a, b: positional-only (must use position) c, d: positional-or-keyword (either style) e, f: keyword-only (must use keyword) """ return a + b + c + d + e + f
# Valid calls:complex_func(1, 2, 3, 4, e=5, f=6) # c,d positionalcomplex_func(1, 2, c=3, d=4, e=5, f=6) # c,d keyword
# Invalid calls:complex_func(a=1, b=2, c=3, d=4, e=5, f=6) # TypeError: a,b positional-onlycomplex_func(1, 2, 3, 4, 5, 6) # TypeError: e,f keyword-only2.6 Why Python Added This Syntax
This syntax was introduced in Python 3.8 via PEP 570. Before this, Python had no way to enforce positional-only parameters in pure Python code (only built-in functions like len() had this capability).
Why positional-only matters:
When I rename internal parameters, I don’t want to break existing code that might be passing them by keyword:
# Version 1def process_data(data, verbose=False): pass
# Version 2 - renamed parameterdef process_data(data, show_progress=False): pass # Anyone calling process_data(data, verbose=True) breaks!
# Version 3 - positional-only prevents thisdef process_data(data, /, show_progress=False): pass # No one could have used verbose=True anywayWhy keyword-only matters:
For functions with many optional parameters, keyword-only ensures callers explicitly name what they’re setting:
# Without keyword-only - dangerous!def configure(timeout=30, retries=3, debug=False, cache=True): pass
configure(60) # Is this timeout or retries? Ambiguous!
# With keyword-only - explicitdef configure(*, timeout=30, retries=3, debug=False, cache=True): pass
configure(timeout=60) # Clear what we're setting2.7 Common Mistake: Confusing * Separator with *args
I initially confused the * separator with *args:
# These are DIFFERENT:
def func1(*, x, y): # * is a separator, x,y are keyword-only pass
def func2(*args): # *args collects arbitrary positional args pass
def func3(*args, x, y): # x,y are keyword-only, args collects extras passThe key distinction: When * appears alone without a name following it, it’s a separator marking keyword-only parameters. When it appears as *args, it’s a parameter that collects extra positional arguments.
3. Summary
In this post, I explained the / and * symbols in Python function parameters:
/marks positional-only parameters: Arguments before/must be passed by position*marks keyword-only parameters: Arguments after*must be passed by keyword- Between
/and*: Parameters accept either positional or keyword syntax
This syntax helps create cleaner APIs: positional-only protects internal parameters from keyword-based breakage during refactoring, while keyword-only forces explicit naming for functions with many optional parameters.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments