If... else skip

Avoid mutable defaults.

1 Like

Cameron and Vladimir have very good points. If the sample solution is from the course authors I would stay away from such a course or at least I would be very cautious!

That is the reason why I wrote:

It is the part in the official documentation which explains the problem.

Though Cameron and Vladimir did not mention that in this case (your and the sample code) the code will work correctly because input_list is only being read in the function. A problem would be if it was modified. Anyway such a sample shows very bad example without an explanation when just a slight modification would make it better (though still needing an explanation):

def unique(input_list=()):
    # input_list is not being modified inside the function so we can provide the default
    # value right in the function arguments definition. To express the intent
    # to not modify the value we use a tuple (immutable) instead of a list (mutable).
    ...

new_stuff = list(x.lower() if isinstance(x, str) else (x) for x in random)

Why are you still creating a list immediately from a generator expression? This is complicating the code and somewhat obscuring your intent. Use a list comprehension which exists for this use:

new_stuff = [x.lower() if isinstance(x, str) else x for x in random]

I also removed the brackets around x for which a saw no reason. To make the code more readable (the conditional expression lowers the readability) you can split it to lines:

new_stuff = [
    x.lower() if isinstance(x, str) else x
    for x in random]
1 Like

By Václav Brožík via Discussions on Python.org at 10Sep2022 10:17:

Though they did not mention that in this case the code will work
correctly because input_list is only being read in the function.

Yes, but it’s really fragile. You’re only one code change from
mistakenly modifying the list. Better to adopt the standard boilerplate
as habit to avoid the situation outright. I’m not a big fan of
explainatory comments which say “this is dodgy, but safe because we
decide not to modify input_list”.

All that said, of course a function might unwisely modify any list it
was given, since it’s just a reference to the list. But a default =[]
looks ok, and isn’t really.

Cheers,
Cameron Simpson cs@cskk.id.au

2 Likes

I’ve seen this referenced in examples before! Now I understand why.
I can’t say that I completely understand how the None aspect works as I just learned it about it, but the instructor said that none is returned when nothing of value is returned from the expression, like printing a print() or something similar. So this would basically mean that as long as what is being called is something that isn’t broken or has value then input_list would be an empty list… thereby preventing possible errors from running through the function? :thinking:

None in this case is just a very safe immutable falsy value that you for sure wouldn’t enter deliberately, nothing else. You’re basically tricking Python into providing you a mutable falsy default data structure from an immutable default constant. It doesn’t have to be None exactly, it’s just an idiomatic expression people that who are knowledgeable in Python will understand immediately, unlike some brain teaser like this:

GIVE_ME_AN_EMPTY_LIST = 0

 def f(input_list=GIVE_ME_AN_EMPTY_LIST):
     if not input_list:
         input_list = []

None is a common placeholder value in Python for “no value”. There’s no actual “no value” in Python, so the singleton object None is used for this purpose. It isn’t a trick, just a value known to not be useful as a “real” value for that parameter.