Python类属性不一致

2024-04-30 05:49:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图理解类属性在Python中是如何工作的。根据下面的例子,我感到困惑。你知道吗

#!/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)

预期输出:

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

看到的输出:

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

为什么共享列表和整数的不同副本之间不一致?你知道吗

下面是另一个示例,它显示了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')

基于先前输出的预期输出:

1 Eve
1 Abel
1 Eva

实际输出:

1 Eve
2 Abel
3 Eva

Tags: textnameselfadditemsvalb2b1
2条回答

listitems是类的所有实例中的一个共享对象。你用self.items.append(x)修改它,但你永远不会创建另一个列表。所以每个对.items的引用都在共享对象Bag.items上工作

但是,当你这么做的时候

b1.val += 1

这就像写作

b1.val = b1.val + 1

因为b1还没有val的单独值,所以得到的效果是:

b1.val = Bag.val + 1

您正在为b1.val分配一个不同于Bag.val的新值。你知道吗

所以你的实例有一个共享的items,但是有单独的val,因为你实际上把分配给了b1.val等等

我将把你的例子扩展一下

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}

循序渐进:

  1. self.items.append(x)

    首先,python尝试查找在object (self.__dict__)中是否有items,如果没有,则尝试从类作用域中查找items,并附加它。此表达式不返回任何内容。self.__dict__在这个表达式之后是不变的。

  2. self.val += 1

    这是int的扩充赋值。因此__iadd__将被调用,如果没有实现,则__add__将被调用,它将始终返回一个新的int。旧的int不会更改,因为它是不可变的。详细说明

    你知道吗自我价值= 自我价值+1个

    rhs上的self.val第一次引用class属性(因为b1.dict没有这个属性),它创建了一个新的int,这个int现在存储在对象的__dict__(因为lhs)。。在rhs中的第二次self.val引用了self.__dict__中的val,如果您本想做Bag.val += 1,那么它将始终操作类变量(就像您的第二个示例)

  3. self.items1 += [x]

所以这也是list.__iadd__的增加。self.items1对可变序列进行适当更改,对同一列表的引用也作为此表达式的一部分返回。因此,在这个语句之后,您应该看到self.__dict__将包含items1,但内容与Bag.__dict__['items1']相同。你知道吗

你的第二个例子完全不同:

cls.cl_roll += 1 

此语句始终操作类变量。你知道吗

相关问题 更多 >