如何避免在实例间共享类数据?

我想要的是这样的行为:

class a: list=[] y=a() x=a() x.list.append(1) y.list.append(2) x.list.append(3) y.list.append(4) print x.list [1,3] print y.list [2,4] 

当然,当我印刷的时候真正发生的是:

 print x.list [1,2,3,4] print y.list [1,2,3,4] 

显然他们正在分享a类中的数据。 我如何获得单独的实例来实现我所希望的行为?

你要这个:

 class a: def __init__(self): self.list = [] 

在类声明中声明variables使得它们成为“类”成员而不是实例成员。 在__init__方法中声明它们可以确保在对象的每个新实例旁边都创build了一个新的成员实例,这是您正在查找的行为。

您将“list”声明为“类级属性”,而不是“实例级属性”。 为了使属性在实例级别有效,需要通过引用__init__方法中的“self”参数(或其他地方根据情况)来初始化它们。

您不必严格地在__init__方法中初始化实例属性,但它使理解更容易。

接受的答案有效,但多一点解释不会伤害。

创build实例时,类属性不会成为实例属性。 当赋值给它们时,它们变成实例属性。

在原始代码中,在实例化之后没有赋值给list属性; 所以它仍然是一个类属性。 定义__init__列表是__init__因为在实例化之后调用__init__ 。 或者,这个代码也会产生所需的输出:

 >>> class a: list = [] >>> y = a() >>> x = a() >>> x.list = [] >>> y.list = [] >>> x.list.append(1) >>> y.list.append(2) >>> x.list.append(3) >>> y.list.append(4) >>> print(x.list) [1, 3] >>> print(y.list) [2, 4] 

但是,这个问题中令人困惑的场景永远不会发生在诸如数字和string这样的不可变对象上,因为它们的值在没有赋值的情况下是不能改变的。 例如,类似于原始string属性types的代码可以毫无问题地工作:

 >>> class a: string = '' >>> x = a() >>> y = a() >>> x.string += 'x' >>> y.string += 'y' >>> x.string 'x' >>> y.string 'y' 

所以总结一下: 当且仅当在实例化之后,在__init__方法中赋值给它们的时候,类属性才会变成实例属性 。 这是一件好事,因为如果您在实例化之后从不为某个属性赋值,那么您可以拥有静态属性。

是的,你必须在“构造函数”中声明,如果你想列表成为一个对象属性而不是一个类属性。

所以几乎所有的答复似乎都错过了一个特定的观点。 类variables不会成为实例variables,如下面的代码所示。 通过使用元类来截获类级别的variables赋值,我们可以看到,当a.myattr被重新分配时,类中的字段赋值魔术方法不会被调用。 这是因为该分配创build了一个新的实例variables 。 这种行为与类variables完全无关 ,如第二类所示,它没有类variables,但仍然允许字段赋值。

 class mymeta(type): def __init__(cls, name, bases, d): pass def __setattr__(cls, attr, value): print("setting " + attr) super(mymeta, cls).__setattr__(attr, value) class myclass(object): __metaclass__ = mymeta myattr = [] a = myclass() a.myattr = [] #NOTHING IS PRINTED myclass.myattr = [5] #change is printed here b = myclass() print(b.myattr) #pass through lookup on the base class class expando(object): pass a = expando() a.random = 5 #no class variable required print(a.random) #but it still works 

IN SHORT类variables与实例variables没有任何关系。

更清楚的是,它们恰好处于实例查找的范围之内。 类variables实际上是类对象本身的实例variables 。 如果你想要,也可以有元类variables ,因为元类本身也是对象。 无论是否用于创build其他对象,所有东西都是一个对象,所以不要被其他语言的类语义使用。 在Python中,一个类实际上只是一个对象,用于确定如何创build其他对象以及它们的行为。 元类是创build类的类,只是为了进一步说明这一点。

虽然接受的污水是现货,我想补充一点说明。

让我们做一个小小的练习

首先定义一个类如下:

 class A: temp='Skyharbor' def __init__(self, x): self.x=x def change(self, y): self.temp=y 

那么我们在这里有什么?

  • 我们有一个非常简单的类,它有一个属性temp是一个string
  • 一个init方法设置self.x
  • 一个改变方法设置self.temp

直到目前为止是吗? 现在让我们开始玩这个课程。 我们先来初始化这个类:

 a = A('Tesseract') 

现在执行以下操作:

 >>> print a.temp Skyharbor >>> print A.temp Skyharbor 

那么, a.temp如预期的a.temp工作,但是这个地狱怎么做? 那么它工作,因为临时是一个类属性。 python中的所有东西都是一个对象。 这里A也是类type一个对象。 因此,temp属性是A类所拥有的属性,如果通过A(而不是a的实例)更改temp的值,则更改的值将反映在A类的所有实例中。 让我们继续这样做:

 >>> A.temp = 'Monuments' >>> print A.temp Monuments >>> print a.temp Monuments 

有趣的是不是? 注意id(a.temp)和id(A.temp)仍然是一样的

任何Python对象都会自动给出一个dict属性,其中包含属性列表。 让我们来看看这个字典包含的例子对象:

 >>> print A.__dict__ { 'change': <function change at 0x7f5e26fee6e0>, '__module__': '__main__', '__init__': <function __init__ at 0x7f5e26fee668>, 'temp': 'Monuments', '__doc__': None } >>> print a.__dict__ {x: 'Tesseract'} 

请注意, temp属性列在A类的属性中,而x则列在实例中

那么,如果没有列出实例a,那么我们如何得到a.temp的定义值呢? 那么这是__getattribute__()方法的魔力。 在Python中,虚线语法会自动调用这个方法,所以当我们编写a.temp ,Python会执行一个。 getattribute ('temp')。 该方法执行属性查找操作,即通过在不同的地方查找来查找属性的值。

__getattribute__()的标准实现首先search对象的内部字典( dict ),然后search对象本身的types。 在这种情况下a.__getattribute__('temp')首先执行a.__dict__['temp'] ,然后a.__class__.__dict__['temp']

好,现在让我们使用我们的change方法:

 >>> a.change('Intervals') >>> print a.temp Intervals >>> print A.temp Monuments 

那么现在我们已经使用self了, print a.temp给了我们与print A.temp不同的值。

现在,如果我们比较id(a.temp)和id(A.temp),它们将会不同

我认为提供的答案是误导性的。 无论您如何定义对象,在类中定义的属性都将成为实例属性。 所以a.list拷贝是做的, x.listy.list是不同的拷贝。 他们似乎是一样的原因是他们都是同一个名单的别名。 但这是列表工作方式的结果,而不是class级的工作方式。 如果你用数字而不是列表来做同样的事情(或者只是使用+ =而不是附加,这会创build一个新的列表),你会看到改变x.attr不会影响改变y.attr

__init__定义self.list ,因为函数被调用两次,每次实例化对象一次,所以创build两个不同的列表。