Reputation: 33
Running the sample code below:
class S:
i = 0
a = []
def __init__(self):
self.i += 1
self.a.append(1)
s1 = S()
print((s1.i, s1.a))
s2 = S()
print((s2.i, s2.a))
The output will be:
(1, [1])
(1, [1, 1])
Why is int
S.i
reset to 0 for s2
but the list
S.a
does not reset to empty? I think it has something to do with the immutable int
vs mutable list
but could someone help to express more details what happened to the two class variables during the two init calls?
Upvotes: 3
Views: 1436
Reputation: 17355
So you are altering the instance attributes when you call s1.i
or s1.a
. To change the class attributes try this:
S.i += 1
S.a.append(1)
In your constructor you initialise self.a
and self.i
. this creates instance attributes that belong to each instance of the class.
The a
and the i
declared outside the constructor are class attributes and are shared by all instances.
The reason s1.a
and S.a
updates regardless of which attribute is used is because lists are mutable and both the instance and class variables are references to the same list.
Upvotes: 2
Reputation:
class S:
i = 0
a = []
def __init__(self):
self.i += 1
self.a.append(1)
the list as defined by a = []
is a class attribute. It's instantiated when the class is defined, and remains the same list object. Any instances of this class are going to reference the one list.
If you want to have an empty list for every new instance, then move the list definition to within the __init__
method:
class S:
i = 0
def __init__(self):
self.a = []
self.i += 1
self.a.append(1)
Result:
>>> s1 = S()
>>> print((s1.i, s1.a))
(1, [1])
>>>
>>> s2 = S()
>>> print((s2.i, s2.a))
(1, [1])
Upvotes: 2
Reputation: 21
The way this particular code is written abstracts some of what Python is doing behind the scenes here, so let's go through it.
When you define the class, and you define variables outside of any function like you do at the beginning in your code, it creates class attributes. These are shared among all instances of your class (in your case, s1
and s2
are both sharing the same reference to your i
object and your a
object).
When you initialize the class, you are calling the __init__
function, which, in your code, first calls self.i += 1
, and I think this is where most of the confusion is coming from. In Python, integers are immutable, so they cannot be overridden. By calling +=
, you are removing the reference to your old i
variable and creating a new one referencing a different place in memory. But because you are now in a function in your class, it's being defined as an instance attribute. Instance attributes are not shared among different instances of your class.
However, lists are mutable. So when you append 1
to your list, you are not creating a new instance variable, so it keeps the same reference to the class attribute, and therefore when you initialize your class the second time, it adds it onto the class attribute that already has been populated once when you created the first instance.
Upvotes: 0
Reputation: 198496
self.i += 1
is equivalent to
self.i = self.i + 1
When the instance variable does not exist, the value is looked up on the class, so in this scenario, it is equivalent to
self.i = S.i + 1
After you define self.i
, then any further value lookup is on the instance variable, not on the class variable. So after this line, you have S.i = 0
and s1.i = 1
. Since S.i
is not modified, s2.i
also becomes 1
.
On the other hand,
self.a.append(1)
does not create a new instance variable, but appends an element to the existing class variable.
Upvotes: 1