user11308226
user11308226

Reputation:

Non-Default parameter follows default parameters

I keep getting an error message on my function parameters.

I've seen some other questions that are similar but I struggle to understand the answer and those people also had different situations which were more complex.

def check(user_answer='', list_of_blanks, letters):
    x = 0
    for letter in letters:
        if letter == user_answer:
            list_of_blanks[letters.index(user_answer)] = user_answer
            x += 1
    if x == 0:
        return False
    else:
        str(list_of_blanks)
    list_of_blanks = ''.join(list_of_blanks)
    return list_of_blanks

pycharm highlights "list_of_blanks, letters" (function parameters) and gives me the error which says:

Non-Default parameter follows default parameter.

If I try to default the parameter to an empty list like so:

list_of_blanks=[], letters=[]

then I get this error:

Default argument value is mutable.

Upvotes: 2

Views: 1921

Answers (3)

Mad Physicist
Mad Physicist

Reputation: 114230

Giving an argument a default value makes it optional. You can't have optional arguments before non-optional ones because then it becomes unclear which slots you mean to assign and which you don't. You might be able to come up with a complex set of rules to determine how to interpret the arguments, but Python attempts to avoid unnecessary complexity, ambiguity, and counter-intuitive behavior.

You have a number of options available to work around this:

  1. Move the default argument to the end of the list:

     def check(list_of_blanks, letters, user_answer=''):
    
  2. Make all the other arguments optional as well:

     def check(user_answer='', list_of_blanks=(), letters=()):
    

    Using tuples, which are immutable, avoids the possibility accidental (and permanent) modification of the default lists.

  3. Remove the default argument entirely:

    def check(user_answer, list_of_blanks, letters):
    

    Frankly this seems to be the best option in your case. Conceptually speaking, the most important arguments of a function come first. Unless your function has side-effects, making these arguments optional seems somehow deficient.

  4. Make the other arguments required, but keyword-only:

    def check(user_answer='', *, list_of_blanks, letters):
    

    You will no longer be able to invoke the function as check('abc', ['b', 'c'], ['a']) because list_of_blanks and letters can no longer be specified as positional arguments. You must now invoke with keywords as check('abc', list_of_blanks=['b', 'c'], letters=['a']). But now the first argument is unambiguously optional, so you can do things like check(letters=['a'], list_of_blanks=['b', 'c']).

My personal preference, in decreasing order is 3, 4, 1, 2. I think your best bet is to have no optional arguments, but I can imagine you being in a phase where you'd want to play with keyword-only arguments regardless of the design implications.

Upvotes: 0

Michael Bianconi
Michael Bianconi

Reputation: 5232

You can't have a non-positional argument (param=value) prior to positional arguments.

def func(positional1, positional2, nonpositional=10)

This is due primarily to the fact you are not required to specify the name of non-positional parameters.

func(10, 50, nonpositional=60) == func(10, 50, 60)

Upvotes: 4

skymon
skymon

Reputation: 870

Is it possible to switch positions of the arguments? Then you could just write:

def check(list_of_blanks, letters, user_answer=''):

Upvotes: 2

Related Questions