SyntaxError: non-default argument follows default argument

avatar

Borislav Hadzhiev

Last updated: Apr 20, 2022

banner

Photo from Unsplash

SyntaxError: non-default argument follows default argument #

The Python "SyntaxError: non-default argument follows default argument" occurs when we define a function with a positional parameter that follows a default parameter. To solve the error, make sure to specify all default parameters after the positional parameters of the function.

syntaxerror non default argument follows default argument

Here is an example of how the error occurs.

main.py
# ⛔️ SyntaxError: non-default argument follows default argument def get_employee(first, last='Doe', salary): return {'first': first, 'last': last, 'salary': salary}

The salary positional parameter follows the last default parameter which causes the error.

To solve the error, make sure to specify the default parameters of the function after the positional ones.

main.py
def get_employee(first, salary, last='Doe'): return {'first': first, 'last': last, 'salary': salary} emp_1 = get_employee('James', 100) print(emp_1) # {'first': 'James', 'last': 'Doe', 'salary': 100} emp_2 = get_employee('Alice', 100, 'Smith') print(emp_2) # {'first': 'Alice', 'last': 'Smith', 'salary': 100}

We moved the last default parameter after the salary positional one which solved the error.

Default parameter values are ones that have the form parameter = expression.

When we declare a function with one or more default parameter values, the corresponding arguments can be omitted when the function is invoked.

Positional parameters cannot follow a parameter with a default value because otherwise Python has no way of knowing if we passed an argument for the default parameter or for a positional one.

You can specify parameters in the form *identifier or **identifier after default parameters in the function's definition.

main.py
def get_employee(first, salary, last='Doe', *args, **kwargs): print(args) # 👉️ ('developer', ) print(kwargs) # 👉️ {'country': 'Austria'} return {'first': first, 'last': last, 'salary': salary} emp_1 = get_employee('James', 100, 'Smith', 'developer', country='Austria') print(emp_1) # 👉️ {'first': 'James', 'last': 'Doe', 'salary': 100}

The form *identifier is initialized to a tuple that receives any excess positional arguments.

The form **identifier is initialized to an ordered mapping that receives any excess keyword arguments.

However, note that you might get a confusing result if you omit the value for the last default parameter.

main.py
def get_employee(first, salary, last='Doe', *args, **kwargs): print(args) # 👉️ () print(kwargs) # 👉️ {'country': 'Austria'} return {'first': first, 'last': last, 'salary': salary} emp_1 = get_employee('James', 100, 'developer', country='Austria') print(emp_1) # 👉️ {'first': 'James', 'last': 'developer', 'salary': 100}

Notice that the last parameter got set to a value of developer.

The best way to deal with this is to use a keyword argument for the developer value when calling the function.

main.py
def get_employee(first, salary, last='Doe', *args, **kwargs): print(args) # 👉️ () print(kwargs) # 👉️ {'department': 'developer', 'country': 'Austria'} return {'first': first, 'last': last, 'salary': salary} emp_1 = get_employee('James', 100, department='developer', country='Austria') print(emp_1) # 👉️ {'first': 'James', 'last': 'Doe', 'salary': 100}
It's always better to use keyword arguments for functions that take multiple arguments, where the order is not clear by the function's name.

Keyword arguments use the format name='value'.

An important note is to avoid setting default values for non-primitive parameters, e.g. dictionaries and lists.

Here is an example of how this can go wrong.

main.py
def get_address(address={}): return address addr1 = get_address() addr2 = get_address() addr1['country'] = 'Germany' print(addr1) # 👉️ {'country': 'Germany'} print(addr2) # 👉️ {'country': 'Germany'}

We called the get_address() function 2 times and stored the results in variables.

Notice that we only set the country key on one of the dictionaries, but both of them got updated.

This is because default parameter values are only evaluated once - when the function is defined.

They are not evaluated each time the function is called.

When a non-primitive default parameter value like a dictionary or list is mutated, it is mutated for all function calls.

One way to get around this issue is to set the default parameter value to None and conditionally update it in the body of the function.

main.py
def get_address(address=None): if address is None: address = {} return address addr1 = get_address() addr2 = get_address() addr1['country'] = 'Germany' print(addr1) # 👉️ {'country': 'Germany'} print(addr2) # 👉️ {}

The body of the function is run every time it is invoked, so the issue no longer exists.

Conclusion #

The Python "SyntaxError: non-default argument follows default argument" occurs when we define a function with a positional parameter that follows a default parameter. To solve the error, make sure to specify all default parameters after the positional parameters of the function.

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.