这是赚取积分的简单方法。请解释以下内容:
class C:
a = {}
b = 0
c = []
def __init__(self):
self.x = {}
def d(self, k, v):
self.x[k] = v
self.a[k] = v;
self.b = v
self.c.append(v)
def out(self, k):
print(self.x[k], self.a[k], self.b, self.c[0])
c = C()
d = C()
c.d(1, 10)
d.d(1, 20)
c.out(1)
d.out(1)
将输出以下内容:
10 20 10 10
20 20 20 10
为什么字典,列表和'普通'变量的表现各不相同?
编辑:我认为问题很明显,但让我更详细地说明一下:
我有一个有三个属性的类,a,b和c。我创建了该类的两个实例。然后我调用一个方法来修改每个实例的这些属性。当我检查属性时,我发现如果一个属性是一个字典,它将在所有实例中共享,而如果它是一个“普通”变量,它的行为就像人们期望的那样,对于每个实例都是不同的。
答案 0 :(得分:5)
首先,[]
不是数组,它是一个列表。这里的问题是属性解析和可变变量如何工作。让我们从
class Foo(object):
a = {}
b = 0
c = []
这将创建一个具有三个属性的类 - 可以通过类本身(例如Foo.a
)或通过类'实例(Foo().a
)获得这些属性。属性存储在名为__dict__
的特殊内容中。类和实例都有一个(有些情况下这不是真的,但它们在这里无关紧要) - 但在Foo
的情况下,当实例是实例时,实例__dict__
为空已创建 - 所以当您执行Foo().a
时,实际上您正在访问与Foo.a
中相同的对象。
现在,您要添加__init__
。
class Foo(object):
# ...
def __init__(self):
self.x = {}
这会创建一个不属于“__dict__
”类的属性,但会在实例1中创建一个属性,因此您无法访问Foo.x
,只能访问Foo().x
。这也意味着x
在每个实例中都是一个完全不同的对象,而类属性由所有类实例共享。
现在你要添加你的变异方法。
class Foo(object):
# ...
def mutate(self, key, value):
self.x[key] = value
self.a[key] = value
self.b = value
self.c.append(value)
您是否记得self.x = {}
创建了一个实例属性?这里self.b = value
做了同样的事情 - 它根本不触及class属性,它创建了一个新的实例,实例掩盖了共享的一个(这就是引用在Python中的工作方式 - 赋值将名称绑定到一个对象) ,并且永远不会修改名称所指向的对象。)
但你没有重新绑定self.a
和self.c
- 你就地改变它们(因为它们是可变的,你可以这样做) - 所以实际上你正在修改原始的类属性,这就是为什么你可以观察另一个实例中的变化(因为它们是由它们共享的)。 self.x
表现不同,因为它不是类属性,而是实例1。
您还只打印self.c
的第一个元素 - 如果您打印所有元素,则会看到[10, 20]
。
答案 1 :(得分:2)
a和c是类属性b,x是实例属性。
您应该阅读并理解Python: Difference between class and instance attributes