我认为我对类和实例变量有一些误解。这是一个示例代码:
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
是类变量,因为我用任何方法声明它们。我以相同的方式修改方法中的值(在他的声明中使用self
,可能不正确?)。但结果显示energy
对每个对象采用不同的值(如实例变量),而skills
似乎是共享的(如类变量)。我想我错过了一些重要的事情......
答案 0 :(得分:38)
这里的诀窍在于了解self.energy -= 1
的作用。它真的有两个表达方式;一个获得self.energy - 1
的值,一个将其分配回self.energy
。
但让你感到困惑的是,在该任务的两边,引用的解释方式不同。当告诉Python获取self.energy
时,它会尝试在实例上找到该属性,失败并回退到class属性。但是,当它分配给self.energy
时,它将始终分配给实例属性,即使之前不存在。
答案 1 :(得分:31)
您正在遇到基于可变性的初始化问题。
首先,修复。 skills
和energy
是类属性。
将它们视为只读是一种很好的做法,作为实例属性的初始值。构建课程的经典方法是:
class Animal(object):
energy = 10
skills = []
def __init__(self,en=energy,sk=skills):
self.energy=en
self.skills=sk
....
然后每个实例都有自己的属性,所有问题都会消失。
第二次,这段代码发生了什么?
当skills
是每个实例时,为什么energy
会共享?
-=
运算符很微妙。它是就地分配如果可能的话。这里的区别在于list
类型是可变的,因此通常会发生就地修改:
In [6]:
b=[]
print(b,id(b))
b+=['strong']
print(b,id(b))
[] 201781512
['strong'] 201781512
因此a1.skills
和a2.skills
是相同的列表,也可以Animal.skills
访问。但是energy
是一个不可变的int
,所以修改是不可能的。在这种情况下,会创建一个新的int
对象,因此每个实例都会管理自己的energy
变量副本:
In [7]:
a=10
print(a,id(a))
a-=1
print(a,id(a))
10 1360251232
9 1360251200
答案 2 :(得分:24)
初始创建时,两个属性都是同一个对象:
>>> 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
答案 3 :(得分:6)
通过类访问类变量,而不是通过self:
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)
答案 4 :(得分:3)
其实在你的代码中 a1.work(); 打印a1.energy; 打印a2.energy
当你调用a1.work()时,a1对象的实例变量的创建名称是' energy'。 当口译员来打印a1.energy'它执行对象a1的实例变量。 当口译员来打印a2.energy'它执行类变量,因为你没有改变类变量的值,所以它显示10作为输出。