0

I just expierenced a weird problem I don't understand:

class A:
    a_number = 666

a = A()
a2= A()

a.a_number = 555
print a2.a_number  # => 666

This situation is totally clear to me. But have a look at the next example:

class B:
    a_list = [1,2]

b = B()
b2 = B()

b.a_list[1] = 666
print b2.a_list # => [1,666]

Why do the two different objects contain the same list?

0xAffe
  • 1,156
  • 1
  • 13
  • 29
  • In the first example, they also contain the same `a_number`. It's just that in the first example, you give one of them a new `a_number`, but in the second example, you don't give one a new `a_list`. – user2357112 May 04 '15 at 09:09
  • 1
    BTW, you classes should inherit from `object` if you are in Python 2. Read this: http://stackoverflow.com/questions/4015417/python-class-inherits-object – Marc Tudurí May 04 '15 at 09:18

4 Answers4

1

As all of the other other answers point out, a_list is a class attribute, shared among all instances.

But a_number is also a class attribute, shared among all instances. So, why is this different?

Because of this:

a.a_number = 555

That assignment creates an instance attribute a_number in the a instance, which hides the class attribute. If you did the same thing with the list, you'd get the same effect:

>>> b.a_list = [1,2,3]
>>> b2.a_list
[1,2]

But that's not what you do. Instead, you mutate the list in place:

b.a_list[1] = 666

That's an assignment to b.a_list[1], but it's not an assignment to b.a_list. So, it doesn't create an instance attribute in the b object.

If you could mutate a.a_number in-place, it would also show up in a2.a_number. Since numbers aren't mutable, there's no way to see that directly. But you can see it indirectly by comparing a.a_number is a2.a_number or looking at their ids—although that isn't a perfect test, because a.a_number, a2.a_number = 5, 5, or even a.a_number, a2.a_number = 2+3, 6-1, may still give you the same object, not because they're both still using the class attribute, but because they both now have their own instance attributes both referencing the same number 5 object.*


* The language allows the implementation to reuse the same object whenever it knows the value is immutable. In practice, the major existing implementations do this for small integers, the special singleton constants True, False, and None, and maybe a few strings. With (a default-configured) CPython, 555 and 666 aren't considered small integers, but 5 is.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • That's a good explination of the differences. Thank you! – 0xAffe May 04 '15 at 09:22
  • @0xAffe: This whole idea of being an assignment to `b.a_list[1]` but an in-place mutation on `b.a_list` makes sense once you think about it (or at least once you understand `__setitem__`), but it is different from many other languages (and, for that matter, the whole notion of what assignment and variables mean in Python is different from, say, C++), so most people don't get it immediately… – abarnert May 04 '15 at 09:25
  • 1
    Since numbers are immutable, the language may reuse preexisting objects. This is why `(1+2) is (4-1)` even though the operations are independent. – Yann Vernier May 04 '15 at 09:28
  • @YannVernier: Good point. I didn't want to get into that because he's testing with numbers outside the CPython small-int range, but I think you're right that the answer could be confusing to future readers without it, so I added an explanation. – abarnert May 04 '15 at 09:40
0

They are not two different object. indeed they are single object both b and b2 object points to same list object as lists are mutable if you edit a list of b as same list is referenced by b2

enter image description here

c0d3
  • 117
  • 11
  • This answer is worded in such a way that it implies `b` and `b2` are the same object. You should probably change that. – user2357112 May 04 '15 at 09:10
  • but if I print b and b2, it prints me two different memory adresses. Doesn't this mean they are different objects? – 0xAffe May 04 '15 at 09:13
  • b is not b2 but b.a_list is b2.a_list that is what i meant. so changing b.a_list will also change b2.a_list – c0d3 May 04 '15 at 09:15
0

This is because a_list is class attribute and it's global to all B instances. If you want that different instances of B class have is own list you should define in __init__ like this:

class B:
    def __init__(self):
        self.a_list = [1,2]
Marc Tudurí
  • 1,869
  • 3
  • 18
  • 22
  • 1
    Yes, I already found out I can initialize it in the constructor. But why is a_list a class attribute and a_number isn't? – 0xAffe May 04 '15 at 09:16
  • 1
    Because you altered the list, but you replaced (actually, shadowed) the number. The first difference lies in the assignment (which made the second a_number inside a), the second difference is that the integer is immutable (and thus cannot be altered like list.append does). After the assignment, both a and A contain the attribute a_number, so a doesn't need to inherit it like a2 does. – Yann Vernier May 04 '15 at 09:19
0

The two objects don't even contain the list, actually. When you created the class B, you put a_list inside it. When you're accessing b.a_list, the interpreter finds that b doesn't have a_list (you can verify this by checking b.__dict__) and looks deeper, into the class. The exact same process finds the same list when looking at b2.

Marc Tudurí's answer shows how to create separate lists in every new B instance.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26