def foo(li=[]):
li.append(1)
print(li)
foo([2])
# Out: [2, 1]
foo([3])
# Out: [3, 1]
This code behaves as expected, but what if we don't pass an argument?
foo()
# Out: [1] As expected...
foo()
# Out: [1, 1] Not as expected...
This is because default arguments of functions and methods are evaluated at definition time rather than run time. So we only ever have a single instance of the li
list.
The way to get around it is to use only immutable types for default arguments:
def foo(li=None):
if not li:
li = []
li.append(1)
print(li)
foo()
# Out: [1]
foo()
# Out: [1]
While an improvement and although if not li
correctly evaluates to False
, many other objects do as well, such as zero-length sequences. The following example arguments can cause unintended results:
x = []
foo(li=x)
# Out: [1]
foo(li="")
# Out: [1]
foo(li=0)
# Out: [1]
The idiomatic approach is to directly check the argument against the None
object:
def foo(li=None):
if li is None:
li = []
li.append(1)
print(li)
foo()
# Out: [1]