Python:理解类和实例变量

neo*_*urb 56 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']
Run Code Online (Sandbox Code Playgroud)

我认为,energyskill是类变量,因为我宣布出来的任何方法.我以相同的方式在方法中修改它的值(self在他的声明中,可能不正确?).但结果告诉我,energy每个对象(如实例变量)采取不同的值,而skills似乎是共享(如类变量).我想我错过了一些重要的事情......

Dan*_*man 38

这里的诀窍在于理解是什么self.energy -= 1.这真的是两个表达方式; 一个获得价值self.energy - 1,一个分配回来self.energy.

但令你困惑的是,在该任务的两边都没有对引用进行相同的解释.当告诉Python获取时self.energy,它会尝试在实例上找到该属性,失败并回退到class属性.但是,当它分配时self.energy,它将始终分配给实例属性,即使之前不存在.


B. *_* M. 32

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

首先,修复.skills并且energy是类属性.将它们视为只读是一种很好的做法,作为实例属性的初始值.构建课程的经典方法是:

class Animal(object):
    energy = 10
    skills = []
    def __init__(self,en=energy,sk=skills):
        self.energy=en
        self.skills=sk
   ....
Run Code Online (Sandbox Code Playgroud)

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

第二,这段代码发生了什么?为什么skills共享,何时energy是每个实例?

-=操作是很微妙的.它是为了就地分配,如果可能的话.这里的区别是list类型是可变的,因此通常会发生就地修改:

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

[] 201781512
['strong'] 201781512
Run Code Online (Sandbox Code Playgroud)

所以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
Run Code Online (Sandbox Code Playgroud)


Chr*_*man 24

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

>>> a1 = Animal()
>>> a2 = Animal()
>>> a1.energy is a2.energy
True
>>> a1.skills is a2.skills
True
>>> a1 is a2
False
Run Code Online (Sandbox Code Playgroud)

当你分配到一个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
Run Code Online (Sandbox Code Playgroud)

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']
Run Code Online (Sandbox Code Playgroud)

最后,如果要删除实例属性a1.skills,则引用将恢复为class属性:

>>> a1.skills
['sit', 'jump']
>>> del a1.skills
>>> a1.skills
['bark', 'sleep']
>>> id(a1.skills)
140668681481032
Run Code Online (Sandbox Code Playgroud)


Per*_*nce 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)
Run Code Online (Sandbox Code Playgroud)