字典和数组作为类与实例变量

mar*_*tin -1 python

这是获得积分的简单方法.请解释以下内容:

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)
Run Code Online (Sandbox Code Playgroud)

将输出以下内容:

10 20 10 10
20 20 20 10
Run Code Online (Sandbox Code Playgroud)

为什么字典,列表和"普通"变量的表现各不相同?

编辑:我认为问题很明显,但让我更详细地拼写:

我有一个有三个属性的类,a,b和c.我创建了该类的两个实例.然后我调用一个方法来修改每个实例的这些属性.当我检查属性时,我发现如果一个属性是一个字典,它将在所有实例中共享,而如果它是一个"普通"变量,它的行为就像人们期望的那样,每个实例都是不同的.

Cat*_*lus 5

首先,[]不是数组,它是一个列表.这里的问题是属性解析和可变变量如何工作.让我们开始吧

class Foo(object):
    a = {}
    b = 0
    c = []
Run Code Online (Sandbox Code Playgroud)

这将创建一个具有三个属性的类 - 可以通过类本身(Foo.a例如)或通过类'instance(Foo().a)获得.属性存储在一个特殊的东西中__dict__.类和实例都有一个(有些情况下这不是真的,但它们在这里是无关紧要的) - 但是在Foo实例__dict__中,实例在创建实例时是空的 - 所以当你这样做时Foo().a,实际上你'访问与...相同的对象Foo.a.

现在,你正在添加__init__.

class Foo(object):
    # ...

    def __init__(self):
        self.x = {}
Run Code Online (Sandbox Code Playgroud)

这将创建不在类的属性__dict__,但在实例中的一个,所以你不能访问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)
Run Code Online (Sandbox Code Playgroud)

你还记得self.x = {}创建一个实例属性吗?这里self.b = value完全相同 - 它根本不触及class属性,它创建了一个新的实例,它为实例遮盖了共享属性(这就是引用在Python中的工作方式 - 赋值将名称绑定到一个对象,并且永远不会修改名称指向的对象).

但你不重新绑定self.aself.c-你变异他们就地(因为它们是可变的,你可以做到这一点) -所以实际上你修改原始类的属性,这就是为什么你可以观察到其他情况的变化(因为他们分享了这些).self.x表现不同,因为它不是一个类属性,而是一个实例.

你也只打印第一个元素self.c- 如果你打印全部,你会看到它[10, 20].