In python, a well known edge case occurs if you directly make a mutable type a default argument:
def foo(x=[]): return x
y = foo()
y.append(1)
print foo()
The usual work-around is to default the argument to None
and then set it in the body. However, there's 3 different ways to do this, 2 of which are basically the same but the third is quite different.
def foo(x=None):
if x is None:
x = []
return x
This is what I usually see.
def foo(x=None):
x = [] if x is None else x
return x
Identical semantically. A line shorter, but some people complain that python's ternary is unnatural because it doesn't start with the conditional and recommend avoiding it.
def foo(x=None):
x = x or []
This is the shortest. I only learned about this madness today. I know lisp so this is probably less surprising to me than some python programmers, but I never thought this would work in python. This behavior is different; if you pass something that is not None
but evaluates false (like False
) it will not override the default. It can't be used if the default doesn't evaluate false, so if you have a non-empty list or dict default it cannot be used. But empty lists/dicts are (in my experience) 99% of the cases of interest.
Any thoughts on which is the most pythonic? I realize there is an element of opinion here, but I'm hoping someone can give a good example or reasoning as to what is considered the most idiomatic. Compared to most communities python tends to strongly encourage people to do things a certain way so I'm hoping this question and its answers will be useful even if it's not totally black and white.
I'd go with #1 because it's simpler; its "else" branch is implied. It is harder to misinterpret it.
I'd not go with #3 in this particular case: bool(x)
is equally false for None
, []
, {}
, ()
, 0
and a few other things. If by mistake I pass a 0
into a function that expects a list, it's better if the function fails fast, instead of mistaking the zero for an empty list!
In other cases c and x else y
could be a convenient ternary operator, but you have to control the type of c
; it's easier when it's a local variable and not a function parameter.
If you often find yourself substituting a value for None
, white a function for that. Consider something like x = replace_none(x, [])
.
I'd say the first way is best in the general case. The first and second way are functionally equivalent, but the first will be easier to read for newcomers.
def foo(x=None):
if x is None:
x = []
return x
The x or []
trick can only be used if:
[]
, {}
, None
, my-special-class-with-__bool__
).Ass a side note, the or
trick can be used if the default evaluates to true: x or [1]
will still be [1]
if x
is falsy. But you won't be able to use []
as argument, as it will be replaced by [1]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With