我在python中编写了以下代码:
#!/usr/bin/python3
class A:
l1 = [True for i in range(3)]
l2 = []
z = 3
def __init__(self):
self.l2 = [True for i in range(3)]
a = A()
b = A()
a.l1[0] = False
a.l2[0] = False
b.l1[1] = False
b.l2[1] = False
a.z = 1337
b.z = -1
print(a.l1)
print(a.l2)
print(b.l1)
print(b.l2)
print(a.z)
print(b.z)
输出:
[False, False, True]
[False, True, True]
[False, False, True]
[True, False, True]
1337
-1
我的问题是为什么l1似乎是静态成员而z不是?
在类体中初始化的所有非基本对象都是静态的吗?
我觉得这是“神奇的wtf python”时刻之一,就像将列表作为默认函数参数一样? (至少对于我来说,主要用C语写的人)
答案 0 :(得分:1)
这种行为是Python中新手的一个主要缺陷,这只需要很少的理解来避免它。
class A:
l1 = [True for i in range(3)]
l2 = []
z = 3
def __init__(self):
self.l2 = [True for i in range(3)]
l1
是一个可变对象,它是一个可以就地更改的对象。看看这个问题:
l1
也是一个跨类所有实例的类级属性,除非它在类树,超类或类的实例中被重新定义得更低:
class Sub(A):
l1 = ['new list']
或:
a = A()
a.l1 = ['new list']
这样,您在类层次结构中重新定义了更高的属性,因此您将在类树中收到最低属性。但这不是你要问的。因为z
是一个不可变对象 - 当你编写类似这样的代码时,你不能像列表那样就地更改的对象:
a = A()
a.z = 20
您确实在实例的命名空间中定义了另一个z
,并在类z
中隐藏了A
。但是,对于列表,因为它们是可变的,并且因为您可以在就地中更改它们,所以您将为多个实例获取l1
的一个引用,例如:
A类: l1 = [1,2,3]
>>> A().l1.append(4)
>>> A().l1 #Another instance created but same object l1
[1, 2, 3, 4]
我们对就地变更有何意义?由于列表是存储其他对象的序列,因此它在技术上具有自身的引用以及其嵌入对象的其他引用。就地分配更改列表中嵌入对象的引用,但不更改列表对象本身的引用。
Python Documentation: 数字对象是不可变的;一旦创造了它们的价值永远不变Python数字当然与数学数字密切相关,但受到计算机中数值表示的限制。
自己研究这段代码,你会注意到差异:
class A:
mutable = [True, False,] # shared by all objects, can be changed in-place
z = 22 # shared by all objects too, can't be changed in-place
def __init__(self):
self.l1 = [] # per-instance attribute
a = A()
b = A()
a.mutable[0] = False # Change A.mutable[0] index only, mutable shared by all objects
a.l1.append('S') # change l1 in-place, l1 not shared by all object
print(b.mutable) # print mutable, [False, False]
print(a.mutable) # print mutable, [False, False]
print(a.l1) # differing list objects, a.l1 is not b.l1
print(b.l1)
print(a.l1 is b.l1) # object identity check: is a.l1 same object as b.l1?
print(a.mutable is b.mutable) # is a.mutable same object as a.mutable?
# changing z
a.z = 24 # create a new int object called z in a
print(a.z) # print z of object a which is 24
print(A.z) # print A.Z which is 22
print(b.z) # print class's z, because b object doesn't have z itself
*注意:如果您通常使用列表或可变对象来解决它们的值;在实践中,这将导致更大的应用程序的非常令人费解的行为。确定在更大的应用程序中更改可变对象的值的哪一行代码是非常繁琐的工作,这将加强调试过程。我总是遵循这个建议,我认为每个人都应该这样做!