Python class attribute inconsistency

巧了我就是萌 提交于 2020-01-08 04:52:46

问题


I am trying to understand how class attributes work in Python. I have confusion based on following example.

#!/usr/bin/python3

class Bag:
    val = 100
    items = []

    def add(self, x):
        self.items.append(x)
        print(self.items)

b1 = Bag()
b1.add('book')
b1.val += 1
print(b1.val)

b1.add('pen')
b1.val += 1
print(b1.val)

b2 = Bag()
b2.add('sketches')
b2.val += 1
print(b2.val)

b2.add('text')
b2.val += 1
print(b2.val)

b2.add('canvas')
b2.val += 1
print(b2.val)

Output expected:

['book']
101
['book', 'pen']
102
['book', 'pen', 'sketches']
103
['book', 'pen', 'sketches', 'text']
104
['book', 'pen', 'sketches', 'text', 'canvas']
105

Output seen:

['book']
101
['book', 'pen']
102
['book', 'pen', 'sketches']
101
['book', 'pen', 'sketches', 'text']
102
['book', 'pen', 'sketches', 'text', 'canvas']
103

Why is there inconsistency between list being shared vs different copies of integers?

Here is another example that shows a different behavior for int.

#!/usr/bin/python3

class Person:
    cl_roll = 0

    def __init__(self, name):
        self.name = name
        self.roll = self.next_roll()
        print(self.roll, self.name)

    @classmethod
    def next_roll(cls):
        cls.cl_roll += 1
        return cls.cl_roll

p1 = Person('Eve')
p2 = Person('Abel')
p3 = Person('Eva')

Output expected based on previous output:

1 Eve
1 Abel
1 Eva

Actual output:

1 Eve
2 Abel
3 Eva

回答1:


I will extend your example a bit

class Bag:
   items = []
   items1 = []
   val = 100
   def add(self, x):
       self.items.append(x)
       self.val += 1
       self.items1 += [x]


b1 = Bag()
print(Bag.__dict__)
#op-1>>> 'items': [], 'items1': [], 'val': 100,
print(b1.__dict__)
#op-2>>> {}

b1.add(111)
print(Bag.__dict__)
#op-3>>> {'items': [111], 'items1': [111], 'val': 100}
print(b1.__dict__)
#op-4>>>{'items1': [111], 'val': 101}

To go step-by-step:

  1. self.items.append(x):

    First, python tries to find if we have a items in the object (self.__dict__), if not then it tries to find items from the class scope and appends it. Nothing is returned as part of this expression. self.__dict__ is untouched after this expression.

  2. self.val += 1

    This is augmented assignment for int. So __iadd__ will be called, if this is not implemented 's __add__ will be called which will always return a new int. The old int is not changed in-place because it is immutable. To elaborate

    self.val = self.val + 1

    The first time self.val on the rhs refers to the class attribute (since b1.dict does not have it) and it creates a new int which is now stored in the object's __dict__ (because of lhs).. The second time self.val in the rhs refers to the val in self.__dict__ Instead if you would have done Bag.val += 1, then it will always manipulate the class variable (like your second example)

  3. self.items1 += [x]

So this is also augmented addition of the list.__iadd__. self.items1 is changed in-place for mutable sequences and the reference to the same list is also returned as part of this expression. So, after this statement, you should see that self.__dict__ will contain items1 but with same contents as Bag.__dict__['items1'].

Your second example is altogether different:

cls.cl_roll += 1 

this statement always manipulates the class variable.




回答2:


The list items is one shared object among all the instances of your class. You modify it with self.items.append(x) but you don't ever create another list. So every reference to .items is working on the shared object Bag.items

However, when you do

b1.val += 1

This is like writing

b1.val = b1.val + 1

And because b1 does not yet have an individual value for val, the effect you get is:

b1.val = Bag.val + 1

You are assigning to b1.val a new value that is different from Bag.val.

So your instances have a shared items, but have individual val, because you actually assign to b1.val etc.



来源:https://stackoverflow.com/questions/57901837/python-class-attribute-inconsistency

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!