1
class Foo:
    def __init__(self, stuff = []):
        print(stuff)
        self.stuff = stuff
    def add(self,x):
        self.stuff.append(x)


>>>f = Foo()
[]
>>>f.add(1)
>>>g = Foo()
[1]

And I make only one change to the code(line 4)

class Foo:
    def __init__(self, stuff = []):
        print(stuff)
        self.stuff = stuff or []
    def add(self,x):
        self.stuff.append(x)


>>>f = Foo()
[]
>>>f.add(1)
>>>g = Foo()
[] 

I change something in the line 4 but resulting in the change in the printing outcome(which is in the line 3)

I just want to know how it works.

user2901156
  • 69
  • 1
  • 5

2 Answers2

1

In this line:

def __init__(self, stuff = []):

all instances created with Foo() will use the same list, created as the default value for all calls of __init__. That's why in the first example adding anything to f results in modifying the default list for all further instance of Foo. This is because the part stuff = [] is evaluated when the interpreter evaluates the def line. From the documentation:

Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.

(bold added by me).

In the second example, as the default is always empty the second part of the or clause is evaluated which creates a new, individual instance of a list each time a new instance of Foo is created. So all Foo instances will have their own lists.

Here you can find more explanations and examples.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
1

The list you pass as fallback parameter value is instantiated once and reused throughout every call to __init__. So adding a value into it will propagate to every next instance of Foo.

The difference between the first and second examples is stuff or []. You need to know that an empty list, when evaluated as a boolean value is False, so the stuff or [] expression returns the second operand if stuff is empty, which will always be the case in your example. The second operand is a list that gets instantiated in the method call, so is a different instance everytime __init__ is called. This ensures that the added value doesn't propagate.

matehat
  • 5,214
  • 2
  • 29
  • 40