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']
我认为energy
和skill
是阶级variables,因为我宣布他们没有任何方法。 我用同样的方法在自己的声明中修改自己的值(也许在自己的声明中,也许不正确?)。 但是结果表明, energy
对每个对象都有不同的值(像一个实例variables),而skills
似乎是共享的(就像一个类variables)。 我想我错过了一些重要的东西
您正在运行基于可变性的初始化问题。
首先 ,修复。 skills
和energy
是阶级属性。 将它们视为只读,作为实例属性的初始值是一种很好的做法。 build立你的课程的经典方法是:
class Animal(object): energy = 10 skills = [] def __init__(self,en=energy,sk=skills): self.energy=en self.skills=sk ....
那么每个实例都会有自己的属性,所有的问题都会消失。
其次 ,这个代码发生了什么? 为什么skills
是共享的,当energy
是每个实例?
-=
运算符是微妙的。 如果可能的话,它是用于就地分配的。 这里的区别是list
types是可变的,所以经常发生就地修改:
In [6]: b=[] print(b,id(b)) b+=['strong'] print(b,id(b)) [] 201781512 ['strong'] 201781512
所以a1.skills
和a2.skills
是相同的列表,这也可以作为Animal.skills
访问。 但energy
是一个不可变的int
,所以修改是不可能的。 在这种情况下,会创build一个新的int
对象,所以每个实例pipe理自己的energy
variables副本:
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作为输出。