我想要的是这种行为:
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]
Run Code Online (Sandbox Code Playgroud)
当然,我打印时真正发生的是:
print(x.list) # prints [1, 2, 3, 4]
print(y.list) # prints [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)
显然,他们正在课堂上分享数据a.如何获得单独的实例来实现我想要的行为?
aby*_*byx 136
你要这个:
class a:
def __init__(self):
self.list = []
Run Code Online (Sandbox Code Playgroud)
声明类声明中的变量使它们成为"类"成员而不是实例成员.在__init__方法中声明它们可确保在对象的每个新实例旁边创建成员的新实例,这是您正在寻找的行为.
jur*_*eza 19
接受的答案有效,但更多的解释不会受到伤害.
创建实例时,类属性不会成为实例属性.它们在为其分配值时成为实例属性.
在原始代码中list,实例化后没有为属性赋值; 所以它仍然是一个类属性.在__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]
Run Code Online (Sandbox Code Playgroud)
但是,问题中的混淆场景永远不会发生在诸如数字和字符串之类的不可变对象上,因为如果没有赋值,它们的值就无法更改.例如,类似于具有字符串属性类型的原始代码的工作没有任何问题:
>>> class a:
string = ''
>>> x = a()
>>> y = a()
>>> x.string += 'x'
>>> y.string += 'y'
>>> x.string
'x'
>>> y.string
'y'
Run Code Online (Sandbox Code Playgroud)
总结一下:当且仅当在实例化之后为它们分配值时,类属性才变为实例属性,__init__无论是否在方法中.这是一件好事,因为如果您在实例化后从未为属性赋值,则可以使用静态属性.
jld*_*ont 11
您将"list"声明为"类级属性"而不是"实例级属性".为了在实例级别定义属性,您需要通过__init__方法中的"self"参数(或根据具体情况在其他地方)引用来初始化它们.
您不必严格必须初始化方法中的实例属性,__init__但这样可以更容易理解.
虽然接受的anwer是现货,但我想补充一点描述.
我们来做一个小练习
首先定义一个类如下:
class A:
temp = 'Skyharbor'
def __init__(self, x):
self.x = x
def change(self, y):
self.temp = y
Run Code Online (Sandbox Code Playgroud)
那么我们在这里有什么?
temp一个字符串属性__init__设定的方法self.x self.temp到目前为止相当直接是啊?现在让我们开始玩这个课程.让我们首先初始化这个类:
a = A('Tesseract')
Run Code Online (Sandbox Code Playgroud)
现在执行以下操作:
>>> print(a.temp)
Skyharbor
>>> print(A.temp)
Skyharbor
Run Code Online (Sandbox Code Playgroud)
好吧,a.temp按预期工作,但到底是怎么回事A.temp?它工作正常,因为temp是一个类属性.python中的所有东西都是一个对象.这里A也是阶级的对象type.因此,属性temp是由A类保持的属性,如果您通过A(而不是通过实例a)更改temp的值,则更改的值将反映在A类的所有实例中.让我们继续这样做:
>>> A.temp = 'Monuments'
>>> print(A.temp)
Monuments
>>> print(a.temp)
Monuments
Run Code Online (Sandbox Code Playgroud)
有意思不是吗?并注意到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'}
Run Code Online (Sandbox Code Playgroud)
请注意,temp属性列在A类的属性中,同时x为实例列出.
那么为什么我们得到一个定义的值,a.temp如果它甚至没有为实例列出a.那是__getattribute__()方法的魔力.在Python中,点缀语法会自动调用此方法,因此在编写时a.temp,Python会执行a.__getattribute__('temp').该方法执行属性查找操作,即通过查看不同的位置来查找属性的值.
__getattribute__()搜索的标准实现首先是对象的内部字典(dict),然后是对象本身的类型.在这种情况下a.__getattribute__('temp')首先执行a.__dict__['temp']然后执行a.__class__.__dict__['temp']
好的,现在让我们使用我们的change方法:
>>> a.change('Intervals')
>>> print(a.temp)
Intervals
>>> print(A.temp)
Monuments
Run Code Online (Sandbox Code Playgroud)
那么现在我们已经使用了self,print(a.temp)给了我们不同的价值print(A.temp).
现在,如果我们比较id(a.temp)和id(A.temp),他们也会有所不同.
小智 5
因此,几乎这里的每一个回应似乎都忽略了一个特定的要点。类变量永远不会成为实例变量,如下面的代码所示。通过利用元类在类级别拦截变量赋值,我们可以看到当a.myattr被重新赋值时,类上的字段赋值魔术方法没有被调用。这是因为赋值创建了一个新的实例变量。此行为与类变量完全无关,如第二个类所示,它没有类变量,但仍然允许字段赋值。
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
Run Code Online (Sandbox Code Playgroud)
简而言之,类变量与实例变量无关。
更清楚地说,它们恰好在实例的查找范围内。类变量实际上是类对象本身的实例变量。如果您愿意,您也可以拥有元类变量,因为元类本身也是对象。一切都是对象,无论它是否用于创建其他对象,因此不要受到其他语言使用该词类的语义的束缚。在Python中,类实际上只是一个对象,用于确定如何创建其他对象以及它们的行为。元类是创建类的类,只是为了进一步说明这一点。