如何避免在实例之间共享类数据?
我想要的是这样的行为:
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.list
和y.list
是不同的拷贝。 他们似乎是一样的原因是他们都是同一个名单的别名。 但这是列表工作方式的结果,而不是class级的工作方式。 如果你用数字而不是列表来做同样的事情(或者只是使用+ =而不是附加,这会创build一个新的列表),你会看到改变x.attr
不会影响改变y.attr
。
在__init__
定义self.list
,因为函数被调用两次,每次实例化对象一次,所以创build两个不同的列表。