2
class abc :
    x = 10
    list = []   
    def __init__(self):
        self.a = 30      
        self.b = 40

a = abc()
b = abc()
a.x = a.x + 1
print a.x
print b.x
a.list.append(1)
print b.list   

Output :
10
11
[1]

So we see that x is not shared across objects a and b but list is shared. Can someone explain this behaviour?

So its appears answer to this lies in the fact that list are mutable objs and numbers are not:

class abc :
   x = 10
   m_list = []

   def __init__(self):
       self.a = 30      
       self.b = 40



a = abc()
b = abc()
print id(a.x)
a.x = a.x + 1
print id(a.x)
print a.x
print b.x
print id(a.m_list)
a.m_list.append(1)
print id(a.m_list)
print b.m_list
print id(b.m_list)


output :
5342052
5342040
11
10
38600656
38600656
[1]
38600656

but this is so strange ...numbers are immutable ?
JackSparrow
  • 187
  • 1
  • 2
  • 15

3 Answers3

6

They are both shared across all instances of the class, however the difference is that lists are mutable, but integers are immutable.

This:

a.x = a.x + 1

creates a new immutable int object* then points a.x (but not b.x) to the new object.

By contrast, this:

a.list.append(1)

modifies the mutable list object a.list (and b.list) was already pointing to in-place.

You can modify immutable class attributes more consistently using e.g.

abc.x = abc.x + 1
abc.list.append(1)

And don't call your own variable list!

* small integers are "interned" in CPython, but let's not worry about that for now

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • 1
    Also, if you use a tuple `()` instead of a list `[]`, you will have the same behavior as the int because tuples are immutable. – Nico Apr 17 '14 at 15:40
  • I think something went wrong when copying the line `abc.x = abc.x += 1`-- is that first `=` part meant to be there? – DSM Apr 19 '14 at 19:39
2

a list is a mutable datastructure since you are creating it at the class level it works simillar to static variables in other languages.... however since integers are not mutable when you change a.x you are actually creating a new a.x value and not affecting b.x

class abc :
    x = 10  
    def __init__(self):
        self.a = 30      
        self.my_list = [] #now its tied to the instance rather than the class
        self.b = 40

you can actually see that a.x is not the same object after incrementing it

>>> id(a.x)
5321432
>>> a.x += 1
>>> id(a.x)
5321420  #notice the address of the object is different
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
  • Thanks @Joran .....thats seems to be very helpful ...if possible can you please also explain me the difference between creating variables in python and than in java ... – JackSparrow Apr 17 '14 at 15:51
-1

It has nothing to do with lists, but with the fact that, in your example, a.x is updated and that creates an instance-level variable which is not anymore shared amongst instances.

But the same thing happens to the list, if you update it. To prove my assertion, I will make use of the id() primitive which returns object's unique IDs:

class abc :
    x = 10
    my_list = []

    def __init__(self):
        self.a = 30
        self.b = 40

a = abc()
b = abc()
print "id(a.x) = %d" % id(a.x) # identifier of a.x
print "id(b.x) = %d" % id(b.x) # identifier of b.x = a.x
a.x = a.x + 1
print "id(a.x) = %d" % id(a.x) # changed
print "id(b.x) = %d" % id(b.x) # unchanged
print a.x
print b.x
a.my_list.append(1)
print b.my_list
print "id(a.mylist) = %d" % id(a.my_list)
print "id(b.mylist) = %d" % id(b.my_list) 

a.my_list = [42]
print "id(a.mylist) = %d" % id(a.my_list) # changed
print "id(b.mylist) = %d" % id(b.my_list)

Which gives:

id(a.x) = 154513476
id(b.x) = 154513476
id(a.x) = 154513464  # changed
id(b.x) = 154513476
11
10
[1]
id(a.mylist) = 3072656236
id(b.mylist) = 3072656236
3072781100
3072656236
id(a.mylist) = 3072781100  # changed
id(b.mylist) = 3072656236

As an aside, I have renamed the list variable. Please don't use reserved words as variable names.

Roberto Reale
  • 4,247
  • 1
  • 17
  • 21
  • `a.x = a.x + 1` will throw a `NameError`. – Adam Smith Apr 17 '14 at 15:43
  • It's in the original question. But I ran it and it works. Which python implementation do you use? – Roberto Reale Apr 17 '14 at 15:44
  • Whoops I misread the code. Too many cryptic `a`s. Regardless your answer is wrong, it has EVERYTHING to do with the fact that `abc.my_list` is a mutable object which is shared between instances of the class. Whether that object is a list or a dict or any other mutable is irrelevant, but your assertion that it "creates an instance-level variable which is not anymore shared amongst instances" is malarkey. – Adam Smith Apr 17 '14 at 15:45
  • Are you sure? Please try to call `id(a.x)` before and after the `a.x = a.x + 1`. – Roberto Reale Apr 17 '14 at 15:51
  • Roberto `a.x` is an integer not a list. integers are immutable. try calling `id(a.my_list)` and `id(b.my_list)` though :). Once you REDEFINE `a.my_list`, it turns into a DIFFERENT mutable object since you tied it to the instance. If you replaced `a.my_list.append(42)` with `a.my_list = [42]` in your code, it will break. – Adam Smith Apr 17 '14 at 15:54
  • I know that integers are immutable while lists aren't. But, that's not the point. Indeed, if you call `id(a.my_list)` and `id(b.my_list)`, you'll obtain different IDs after redefining one of the two lists. – Roberto Reale Apr 17 '14 at 16:02
  • I think **I'm** confused. Your answer reads: "But the same thing [that happens to the int] happens to the list, if you update it" but that's patently false. The same thing happens if you assign a BRAND NEW list in its place, but not if you update it. Didn't you admit that in your comment just now? "you'll obtain different IDs after redefining one of the two lists" – Adam Smith Apr 17 '14 at 16:14
  • Also of note, remember that it doesn't ACTUALLY change that list when you redefine it. In your example, `classlst = a.__class__.my_list; a.my_list is classlst; b.my_list is classlst; a.my_list = []; a.my_list is not classlst; b.my_list is classlst` but `a.__class__.my_list` is still the original item. The reason `a.my_list` doesn't fail originally is because it inherits the class's variable by that name. You can overwrite `a.my_list` with any data type and `a.__class__.my_list` will still be an empty list. – Adam Smith Apr 17 '14 at 16:19
  • But isn't exactly that, which the user asked for? When I redefine the list I get a NEW list. When I "redefine" the class variable `a.x` (by `a.x = a.x + 1`) I actually get a new variable which is instance-specific and shadows the class-specific `a.x`. – Roberto Reale Apr 17 '14 at 16:23
  • It's kind of breaking inheritance to define your classes such that they have to implement any instance-specific mutable objects or else they'll change it across all classes. He SHOULD be defining this in `def __init__`. Note that `a.x += 1` looks a lot like `a.my_list.append(1)` but are completely different functionalities, something that's probably not readily apparent to OP. – Adam Smith Apr 17 '14 at 16:27
  • "`a.x += 1` looks a lot like `a.my_list.append(1)` but are completely different functionalities. To this, I fully agree. – Roberto Reale Apr 17 '14 at 16:30
  • Roberto but yes, I suppose if I wanted to do `a = abc(); for var,value in abc.__dict__.items() if type(mutable) in [list,dict,bytearray]: a.__dict__[var] = value`. I think it needs more obfuscation though! :) – Adam Smith Apr 17 '14 at 16:32