How to Resolve Python "SyntaxError: non-default argument follows default argument"
When defining functions in Python, you might encounter the SyntaxError: non-default argument follows default argument
. This error signals a violation of a fundamental rule regarding the order in which function parameters must be defined: parameters with default values can not be followed by parameters without default values.
This guide explains the reasoning behind this rule and shows you how to correctly order your function parameters to resolve the error.
Understanding the Error: Function Parameter Order
Python requires a specific order when defining function parameters to avoid ambiguity when the function is called. When you call a function, Python needs to clearly match the arguments you provide to the parameters defined in the function signature.
Parameters can be:
- Positional (or Required): Must be provided when calling the function; they don't have a default value.
- Default (or Optional/Keyword): Have a default value specified in the definition (
param='default'
); they can be omitted during the function call.
The rule exists because if a required parameter came after an optional one, Python wouldn't know whether an argument provided positionally was intended for the optional parameter or the later required one.
Cause: Incorrect Order in Function Definition
The SyntaxError
is raised during the function definition phase (before you even call the function) if you place a parameter without a default value after one that does have a default value.
# Error Scenario
try:
# ⛔️ SyntaxError: non-default argument follows default argument
# The 'salary' parameter (non-default) comes after 'last' (which has a default).
def create_user(username, is_active=True, email):
pass
except SyntaxError as e:
print(e)
Python detects this invalid ordering immediately when it tries to parse the def
statement.
Solution: Define Non-Default Parameters First
The fix is to reorder the parameters in the function definition so that all parameters without default values come before all parameters with default values.
# ✅ Corrected Order: non-defaults (username, email) before default (is_active)
def create_user(username, email, is_active=True):
"""Creates a user dictionary."""
print(f"Creating user: {username}, Email: {email}, Active: {is_active}")
return {'name': username, 'email': email, 'active': is_active}
# Example Calls:
user1 = create_user('alice', '[email protected]')
# Output: Creating user: alice, Email: [email protected], Active: True
print(f"User 1: {user1}\n")
user2 = create_user('bob', '[email protected]', is_active=False) # Explicitly set optional arg
# Output: Creating user: bob, Email: [email protected], Active: False
print(f"User 2: {user2}\n")
# You can still provide the optional argument positionally if desired
user3 = create_user('charlie', '[email protected]', False)
# Output: Creating user: charlie, Email: [email protected], Active: False
print(f"User 3: {user3}")
By placing email
before is_active=True
, the syntax becomes valid.
How Default Arguments Work
Default arguments make function parameters optional. When defining a function, you assign a default value using =
.
def greet(name, greeting="Hello"):
"""Greets a person, with an optional greeting."""
print(f"{greeting}, {name}!")
greet("World") # Uses default greeting
# Output: Hello, World!
greet("World", "Good morning") # Overrides default greeting
# Output: Good morning, World!
greet(greeting="Hi", name="There") # Keyword arguments work too
# Output: Hi, There!
If the caller doesn't provide a value for greeting
, Python uses "Hello"
. This flexibility requires them to appear after any mandatory (non-default) parameters like name
.
The Required Order of Function Parameters in Python
Python enforces a specific order for different kinds of parameters in a function definition:
- Standard positional-or-keyword parameters without defaults: (e.g.,
a
,b
) - Standard positional-or-keyword parameters with defaults: (e.g.,
c=1
,d='hello'
) - Var-positional parameter:
*args
(collects any extra positional arguments into a tuple) - Keyword-only parameters (required or with defaults): (e.g.,
*, kw_only1
,kw_only2=None
) - Must come after*args
or just*
if*args
isn't present. - Var-keyword parameter:
**kwargs
(collects any extra keyword arguments into a dictionary)
The key takeaway for avoiding the non-default argument follows default argument
error is that group 1 must always come before group 2.
Interaction with *args
and **kwargs
Default arguments must also come before *args
and **kwargs
if they are present.
# ✅ Correct order: standard -> default -> *args -> **kwargs
def process_data(required_arg, optional_arg='default_val', *extra_pos_args, **extra_kw_args):
print(f"Required: {required_arg}")
print(f"Optional: {optional_arg}")
print(f"Extra Positional (*args): {extra_pos_args}")
print(f"Extra Keyword (**kwargs): {extra_kw_args}")
print("-" * 20)
process_data(1)
# Output:
# Required: 1
# Optional: default_val
# Extra Positional (*args): ()
# Extra Keyword (**kwargs): {}
# --------------------
process_data(1, 'option A')
# Output:
# Required: 1
# Optional: option A
# Extra Positional (*args): ()
# Extra Keyword (**kwargs): {}
# --------------------
process_data(1, 'option B', 100, 200, key1='val1', key2='val2')
# Output:
# Required: 1
# Optional: option B
# Extra Positional (*args): (100, 200)
# Extra Keyword (**kwargs): {'key1': 'val1', 'key2': 'val2'}
# --------------------
# Calling with keyword argument to skip default but provide others
process_data(1, 100, 200, optional_arg='skipped_default', key1='val1')
# Output:
# Required: 1
# Optional: skipped_default
# Extra Positional (*args): (100, 200) # 100, 200 go to *args
# Extra Keyword (**kwargs): {'key1': 'val1'}
# --------------------
Important Pitfall: Mutable Default Arguments
A crucial point related to default arguments (though not directly causing this SyntaxError
) is the danger of using mutable objects (like lists []
or dictionaries {}
) as default values. Default values are evaluated only once when the function is defined, not each time it's called.
# --- Problematic Example ---
def add_item(item, my_list=[]): # Default is an empty list, created ONCE
my_list.append(item)
return my_list
list1 = add_item(1)
print(list1) # Output: [1]
list2 = add_item(2) # Uses the *same* list object modified in the previous call!
print(list2) # Output: [1, 2] -- Unexpected!
print(list1 is list2) # Output: True - They are the same object!
# --- Correct Way ---
def add_item_safe(item, my_list=None): # Use None as the default sentinel
"""Safely adds an item to a list, creating a new list if none provided."""
if my_list is None:
my_list = [] # Create a *new* list inside the function call
my_list.append(item)
return my_list
list3 = add_item_safe(1)
print(f"\nSafe List 3: {list3}") # Output: [1]
list4 = add_item_safe(2) # Creates a new list for this call
print(f"Safe List 4: {list4}") # Output: [2] -- Correct!
print(list3 is list4) # Output: False - Different objects
Always use None
as the default for mutable types and create a new instance inside the function if the argument isn't provided.
Conclusion
The SyntaxError: non-default argument follows default argument
is a direct consequence of violating Python's required parameter order during function definition.
To fix it, ensure that all parameters without default values are listed before any parameters with default values in your def
statement. Remember the general order: required positional
-> optional defaults
-> *args
-> keyword-only
-> **kwargs
.
Also, beware of the separate pitfall of using mutable objects like lists or dictionaries as default argument values.