5

Conventional way of dealing with optional list arguments is the following:

def func(list_of_vals = None):
   if list_of_vals is None:
      list_of_vals = []
   ...

I wounder if the following (shorter) version has any pitfalls? why nobody do that? is it considered more obscure?

list_of_vals = list_of_vals or []
Ben Usman
  • 7,969
  • 6
  • 46
  • 66
  • 4
    I do that, I think it's great. Decent python programmers will understand it. But using `l` as a variable name, that's bad. – Alex Hall Oct 28 '16 at 21:37
  • @AlexHall , sure, that's just a sample piece of code to illustrate an idea – Ben Usman Oct 28 '16 at 21:39
  • This is very common. It's standard in JS as well. In fact, default arguments in Typescript compile into the above code, but in JS. Anyone even remotely proficient in the language will know exactly what you're doing. – Carcigenicate Oct 28 '16 at 21:39
  • 10
    If someone explicitly passes an empty list, you create a new one. That may not be what they were expecting. – jonrsharpe Oct 28 '16 at 21:39
  • 1
    @jonrsharpe The fact that an empty list in Python is falsely kind of throws a wrench in the pattern unfortunately. – Carcigenicate Oct 28 '16 at 21:41
  • @jonrsharpe It would only be an issue if the function is supposed to modify its input list in place. But if that's what the function does, it doesn't make sense for the argument to be optional. – Barmar Oct 28 '16 at 21:41
  • 1
    @Barmar yes, if they're also following the *"either mutate **or** return"* pattern it should be fine – jonrsharpe Oct 28 '16 at 21:42
  • @jonrsharpe that is a great comment, you might want to put it as answer so that more people see it – Ben Usman Oct 28 '16 at 21:43

2 Answers2

9

The pattern if arg is None: is usual, it is familiar to Python developers and has no weird edge cases. My recommendation is just stick with the convention.

Your proposal using or logically deviates when bool(list_of_vals) == False but list_of_vals is not None, so I would recommend against doing that.

Another possible option is to "duck type" using an empty tuple:

def a(vals=()):
    ...

Because tuples are immutable, this has none of the pitfalls of the mutable default list. There are many use-cases where you only need to the input container to be indexable and iterable, so vals can happily remain as a tuple.

wim
  • 338,267
  • 99
  • 616
  • 750
  • 1
    I feel like if type of typical values passed to function (`list`) differs from default value type (`tuple`), that might be even more misleading – Ben Usman Oct 28 '16 at 21:41
  • This is an excellent idea. It won't work if the passed list is expected to be modified by the function (which would be strange for an optional argument), but otherwise it even suggests the immutability which is a plus. – zvone Oct 28 '16 at 21:43
  • 1
    @BenUsman A good Python function should care about the type of the argument as little as possible anyway. – zvone Oct 28 '16 at 21:45
  • `vals = vals if vals is not None else []` might be somewhere in between? (both short, readable and conventional) – Ben Usman Oct 28 '16 at 21:50
1

There are two cases, you want that the list can be altered outside the function, then the second variant prevents calling a with:

some_list = []
a(some_list)
print(some_list)

If you want to prevent alternation of the list parameter, you should make a copy inside a

def a(l=()):
    l = list(l)
Daniel
  • 42,087
  • 4
  • 55
  • 81