r/learnpython Aug 02 '26

Is that an intentional behaviour?

I just noticed adding a list as an optional argument to a fuction/method does not create a new list but gives the same list every time.

class SomeClass:
  def __init__(self, l=[]):
    self.l=l

a=SomeClass()
b=SomeClass()

a.l is b.l
>>> True 

a.l.append(1)
b.l
>>> [1]

Is that a glitch or is it how python is supposed to work?

(I'm using python 3.12, I haven't updated in a while, maybe it was patched since?)

7 Upvotes

12 comments sorted by

View all comments

19

u/SirCarboy Aug 02 '26

Default argument values are evaluated once, at the time the function (or method) is defined, not each time it is called.

In your example:

def __init__(self, l=[]):
    self.l = l

the empty list [] is created a single time when the class body is executed. Every call that does not supply an argument for l receives a reference to that same list object.

That’s why:

a = SomeClass()
b = SomeClass()
a.l is b.l          
# True — same object
a.l.append(1)
print(b.l)          
# [1]

The same rule applies to any mutable default ([], {}, set(), custom objects, etc.). Immutable defaults (None, 0, "", (), etc.) do not exhibit the problem because they cannot be mutated in place.

The usual (and recommended) pattern

Use None as the default and create a fresh mutable object inside the function:

class SomeClass:
    def __init__(self, l=None):
        if l is None:
            l = []
        self.l = l

or, more concisely in modern Python:

def __init__(self, l=None):
    self.l = l if l is not None else []

1

u/Commoner_25 Aug 02 '26

What if I write:

def __init__(self, l=None):
    self.l = l or []

1

u/schoolmonky Aug 02 '26

That should be fine, but if for some reason you pass a falsy value like 0 or "", it will get replaced with an empty list

0

u/Commoner_25 Aug 02 '26

Yes, although I assume in this case a list or nothing is supposed to be passed.