我想要的是这样的行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class a: list = [] x = a() y = a() x.list.append(1) y.list.append(2) x.list.append(3) y.list.append(4) print(x.list) # prints [1, 3] print(y.list) # prints [2, 4] |
当然,当我打印时,真正发生的是:
1 2 | print(x.list) # prints [1, 2, 3, 4] print(y.list) # prints [1, 2, 3, 4] |
显然,它们在类
你想要这样的:
1 2 3 | class a: def __init__(self): self.list = [] |
在类声明中声明变量使它们成为"类"成员,而不是实例成员。在
公认的答案是有效的,但多解释一下也无妨。
创建实例时,类属性不会成为实例属性。当赋值给它们时,它们就成为实例属性。
在原始代码中,实例化后没有给
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | >>> 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] |
然而,这个问题中令人困惑的场景永远不会发生在不可变的对象上,比如数字和字符串,因为它们的值不能在没有赋值的情况下更改。例如,一个类似于原始代码的字符串属性类型工作没有任何问题:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> class a: string = '' >>> x = a() >>> y = a() >>> x.string += 'x' >>> y.string += 'y' >>> x.string 'x' >>> y.string 'y' |
因此,总结一下:当且仅当在实例化之后赋值给类属性时(无论是否在
您将"list"声明为"类级属性",而不是"实例级属性"。为了在实例级限定属性的作用域,您需要通过在
严格来说,您不必在
虽然公认的答案是正确的,我想增加一点描述。
我们做个小练习
首先定义一个类如下:
1 2 3 4 5 6 7 8 | class A: temp = 'Skyharbor' def __init__(self, x): self.x = x def change(self, y): self.temp = y |
这是什么?
我们有一个非常简单的类,它有一个属性到目前为止很直接,对吧?现在让我们开始玩这个类。让我们先初始化这个类:
1 | a = A('Tesseract') |
现在做以下事情:
1 2 3 4 | >>> print(a.temp) Skyharbor >>> print(A.temp) Skyharbor |
嗯,
1 2 3 4 5 | >>> A.temp = 'Monuments' >>> print(A.temp) Monuments >>> print(a.temp) Monuments |
很有趣不是吗?注意
任何Python对象都会自动给出一个
1 2 3 4 5 6 7 8 9 10 | >>> print(A.__dict__) { 'change': <function change at 0x7f5e26fee6e0>, '__module__': '__main__', '__init__': <function __init__ at 0x7f5e26fee668>, 'temp': 'Monuments', '__doc__': None } >>> print(a.__dict__) {x: 'Tesseract'} |
注意,
那么,如果没有为实例
好的,现在让我们使用我们的
1 2 3 4 5 | >>> a.change('Intervals') >>> print(a.temp) Intervals >>> print(A.temp) Monuments |
现在我们已经使用了
现在如果我们比较
因此,这里的每一个回答似乎都忽略了一个特定的点。类变量永远不会成为实例变量,如下面的代码所示。通过使用元类在类级别拦截变量赋值,我们可以看到当myattr被重新分配,类上的字段分配魔术方法没有被调用。这是因为赋值创建了一个新的实例变量。这种行为绝对与类变量无关,正如第二个类所演示的那样,该类没有类变量,但仍然允许字段赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 |
简而言之,类变量与实例变量无关。
更明显的是,它们恰好在查找实例的范围内。类变量实际上是类对象本身的实例变量。如果需要,还可以使用元类变量,因为元类本身也是对象。无论是否用于创建其他对象,所有对象都是一个对象,因此不要与word类的其他语言用法的语义绑定在一起。在python中,类实际上只是一个对象,用于确定如何创建其他对象及其行为。元类是创建类的类,只是为了进一步说明这一点。
是的,如果你想让列表变成对象属性而不是类属性,你必须在构造函数中声明。
要保护由其他实例共享的变量,需要在每次创建实例时创建新的实例变量。当您在类中声明一个变量时,它是由所有实例共享的类变量。如果你想让它实例明智的需要使用init方法来重新初始化变量作为引用实例
从Python对象和类由Programiz.com:
__init__() function. This special function gets called whenever a new object of that class is instantiated.This type of function is also called constructors in Object Oriented
Programming (OOP). We normally use it to initialize all the variables.
例如:
1 2 3 4 | class example: list=[] #This is class variable shared by all instance def __init__(self): self.list = [] #This is instance variable referred to specific instance |
我认为所提供的答案具有误导性。在类中定义的属性在对象实例化时成为实例属性,无论如何定义它。因此生成了
在