在下面的代码示例中,我希望a
和b
在每个循环结束时都被删除。
结果是a
被删除了,而b
没有被删除。为什么呢?我如何确保b在每个循环结束时也被删除?
class bag:
a = 5
b = []
def funcie(self):
self.a = self.a + 1
self.b.append(1)
for i in range(5):
inst = bag()
inst.funcie()
print(inst.a)
print(inst.b)
# del inst
输出:
6
[1]
6
[1, 1]
6
[1, 1, 1]
6
[1, 1, 1, 1]
6
[1, 1, 1, 1, 1]
编辑:所以这篇文章解释了为什么b
列表在每个循环中不断增长,即我应该在__init(self)__
函数中声明b
列表。然而,它并没有解释为什么a
变量在每个循环结束时被覆盖,而b
变量却没有。
bag.a
(一个类属性)没有被覆盖,它被a
(一个实例属性)遮蔽,特别是inst
。
Python的一般规则是,如果没有内部/阴影作用域隐藏,则读取将从外部/阴影作用域读取。内部/隐藏作用域是通过赋值创建的,而不仅仅是突变(这只是从变量中读取,然后要求它改变自己)。在作用域和属性的工作方式之间有一些微妙的区别,所以我将只关注属性(因为这是您所询问的)。
当您在bag
的新实例上执行self.a = self.a + 1
时,您读取的self.a
来自class属性,但是在写入self.a
时,您创建了一个新的阴影实例属性。类属性(bag.a == 5
)仍然存在,并且没有被修改,但是从实例的角度来看,它只看到实例属性(inst.a == 6
)。如果您添加了print(bag.a)
,您将看到它从未更改。
相比之下,self.b.append(1)
读取类属性并要求它就地修改自己,这将改变绑定到类属性本身的对象。
self.a = self.a + 1
:
type(self).a = self.a + 1 # Or type(self).a = type(self).a + 1
或者更简单地使用+=
来避免重复更复杂的内容:
type(self).a += 1
数字与list
的不同之处在于Python使用它们的值并将计算结果存储在新的位置。因此,a = a+1
的结果每次都存储在内存中的不同位置。列表保持在相同的内存位置,并在适当的位置更新。以下代码:
class bag:
a = 5
b = []
def funcie(self):
self.a += 1
self.b.append(1)
inst = bag()
print("bag memory ids: ", id(bag.a), id(bag.b))
print("inst memory ids: ", id(inst.a), id(inst.b))
inst.funcie()
print("ids after 1x funcie:", id(inst.a), id(inst.b))
inst.funcie()
print("ids after 2x funcie:", id(inst.a), id(inst.b))
有这样的输出,您可以看到inst.a
的id发生了变化,而inst.b
的id保持不变:
bag memory ids: 9789120 139707042123328
inst memory ids: 9789120 139707042123328
ids after 1x funcie: 9789152 139707042123328
ids after 2x funcie: 9789184 139707042123328
因此,a
的更新值存储在不同的位置,而不改变原始值。