Python:理解类和实例variables

我认为我对类和实例variables有一些误解。 这是一个示例代码:

class Animal(object): energy = 10 skills = [] def work(self): print 'I do something' self.energy -= 1 def new_skill(self, skill): self.skills.append(skill) if __name__ == '__main__': a1 = Animal() a2 = Animal() a1.work() print a1.energy # result:9 print a2.energy # result:10 a1.new_skill('bark') a2.new_skill('sleep') print a1.skills # result:['bark', 'sleep'] print a2.skills # result:['bark', 'sleep'] 

我认为energyskill是阶级variables,因为我宣布他们没有任何方法。 我用同样的方法在自己的声明中修改自己的值(也许在自己的声明中,也许不正确?)。 但是结果表明, energy对每个对象都有不同的值(像一个实例variables),而skills似乎是共享的(就像一个类variables)。 我想我错过了一些重要的东西

您正在运行基于可变性的初始化问题。

首先 ,修复。 skillsenergy是阶级属性。 将它们视为只读,作为实例属性的初始值是一种很好的做法。 build立你的课程的经典方法是:

 class Animal(object): energy = 10 skills = [] def __init__(self,en=energy,sk=skills): self.energy=en self.skills=sk .... 

那么每个实例都会有自己的属性,所有的问题都会消失。

其次 ,这个代码发生了什么? 为什么skills是共享的,当energy是每个实例?

-=运算符是微妙的。 如果可能的话,它是用于就地分配的。 这里的区别是listtypes是可变的,所以经常发生就地修改:

 In [6]: b=[] print(b,id(b)) b+=['strong'] print(b,id(b)) [] 201781512 ['strong'] 201781512 

所以a1.skillsa2.skills是相同的列表,这也可以作为Animal.skills访问。 但energy是一个不可变的int ,所以修改是不可能的。 在这种情况下,会创build一个新的int对象,所以每个实例pipe理自己的energyvariables副本:

 In [7]: a=10 print(a,id(a)) a-=1 print(a,id(a)) 10 1360251232 9 1360251200 

这里的诀窍是了解什么是self.energy -= 1 。 这真是两个表情。 一个获得self.energy - 1的价值,一个将其赋予self.energy

但是让你感到困惑的是,在这个任务的两边都没有同样的解释。 当Python被告知获取self.energy ,它会尝试在实例上find该属性,失败并退回到class属性。 然而,当它赋值给self.energy ,它总是会分配给一个实例属性,即使这个属性以前没有存在过。

在初始创build时,两个属性是相同的对象:

 >>> a1 = Animal() >>> a2 = Animal() >>> a1.energy is a2.energy True >>> a1.skills is a2.skills True >>> a1 is a2 False 

当您分配class属性时,它将在实例中成为本地:

 >>> id(a1.energy) 31346816 >>> id(a2.energy) 31346816 >>> a1.work() I do something >>> id(a1.energy) 31346840 # id changes as attribute is made local to instance >>> id(a2.energy) 31346816 

new_skill()方法不会为skills数组分配一个新值,而是appends修改列表的地方。

如果你要手动添加一个技能,那么skills列表就会来自实例的本地:

 >>> id(a1.skills) 140668681481032 >>> a1.skills = ['sit', 'jump'] >>> id(a1.skills) 140668681617704 >>> id(a2.skills) 140668681481032 >>> a1.skills ['sit', 'jump'] >>> a2.skills ['bark', 'sleep'] 

最后,如果要删除实例属性a1.skills ,则引用将返回到class属性:

 >>> a1.skills ['sit', 'jump'] >>> del a1.skills >>> a1.skills ['bark', 'sleep'] >>> id(a1.skills) 140668681481032 

通过类访问类variables,而不是通过自己:

 class Animal(object): energy = 10 skills = [] def work(self): print 'I do something' self.__class__.energy -= 1 def new_skill(self, skill): self.__class__.skills.append(skill) 

其实在你的代码a1.work(); 打印a1.energy; 打印a2.energy

当你调用a1.work()的时候,一个对象的实例variables会被创build为“能量”。 而当解释器来到'打印a1.energy'它执行对象a1的实例variables。 而当解释器来到“打印a2.energy”它执行类variables,并且因为你没有改变类variables的值,它显示10作为输出。