Why a list is shared and not a variable (class lev

2019-07-19 16:05发布

问题:

This question already has an answer here:

  • How to avoid having class data shared among instances? 8 answers
  • “Least Astonishment” and the Mutable Default Argument 31 answers
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 ?

回答1:

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



回答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


回答3:

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.



标签: python oop