Python:了解类和实例变量

我认为我对类和实例变量有些误解。 这是示例代码:

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是类变量,因为我从任何方法中声明了它们。 我以相同的方式在方法中修改了它的值(他的声明中带有self,也许不正确?)。 但是结果显示我energy为每个对象采用不同的值(如实例变量),而skills似乎是共享的(如类变量)。 我想我错过了重要的事情...

neogurb asked 2019-11-15T14:01:53Z
5个解决方案
38 votes

这里的窍门是了解self.energy的功能。 这实际上是两个表达式; 一个获得self.energy的值,另一个将其分配回self.energy

但是令您感到困惑的是,在该分配的两边,引用的解释方式都不相同。 当Python被告知要获取self.energy时,它将尝试在实例上找到该属性,然后失败,然后回退到class属性。 但是,当它分配给self.energy时,它将始终分配给一个实例属性,即使该属性以前不存在。

Daniel Roseman answered 2019-11-15T14:03:45Z
32 votes

您正在遇到基于可变性的初始化问题。

首先,解决。 a1.skillsa2.skills是类属性。最好将它们视为只读,作为实例属性的初始值。 建立课程的经典方法是:

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

然后,每个实例将具有其自己的属性,所有问题都将消失。

第二,这段代码发生了什么?当每个实例a2.skills时,为什么共享a1.skills

a1.skills操作符很细。 如果可能,它用于就地分配。 此处的区别在于a2.skills类型是可变的,因此经常发生就地修改:

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

[] 201781512
['strong'] 201781512

因此a1.skillsa2.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
B. M. answered 2019-11-15T14:03:06Z
24 votes

最初创建时,两个属性都是同一个对象:

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

当您分配a1.skills属性时,该属性将成为实例的本地属性:

>>> 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

a1.skills方法不会为skills数组分配新值,而是为appends分配新值,以修改列表。

如果要手动添加技能,则a1.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
ChrisFreeman answered 2019-11-15T14:04:50Z
6 votes

通过类而不是通过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)
Periodic Maintenance answered 2019-11-15T14:05:22Z
3 votes

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

当您调用a1.work()时,将为a1对象创建一个实例变量,其名称与“ energy”相同。当解释器开始“打印a1.energy”时,它将执行对象a1的实例变量。当解释器开始“打印a2.energy”时,它将执行类变量,并且由于您尚未更改类变量的值,因此它将显示10作为输出。

Mahendra Vishwakarma answered 2019-11-15T14:06:05Z
translate from https://stackoverflow.com:/questions/35766834/python-understanding-class-and-instance-variables