从Python中的实例方法更改静态变量

时间:2014-01-05 13:30:02

标签: python

我正在尝试在python中更改静态变量

>>> class A():
...     i = 0
...     def add_i(self):
...             self.i = self.i + 1
...
>>> A.i
0
>>> a = A()
>>> a.add_i()
>>> A.i
0
>>> a.i
1

当我致电a.add_i()时,为什么不增加“静态”变量i

6 个答案:

答案 0 :(得分:8)

当您分配到self.i时,您正在创建名为i的新实例变量:

>>> print id(A.i), id(a.i)
9437588 9437576

以下内容将更改A.i而不是重新绑定a.i

A.i = A.i + 1

或者更短:

A.i += 1

答案 1 :(得分:2)

以下代码准确显示了发生的情况:

class A():
    i = 0
    def add_i(self):
        print '\n-----In add_i function-----'
        print 'BEFORE: a.__dict__ :',a.__dict__
        print 'BEFORE: id(self)   : %d\n' % id(self.i)
        self.i = self.i + 1
        print 'self.i = self.i + 1  done\n'
        print 'AFTER: a.__dict__ :',a.__dict__
        print 'AFTER: id(self)   : %d' % id(self.i)
        print '-----end of add_i function-----\n'

a = A()

print '\nA.i     ==',A.i
print 'id(A.i) ==',id(A.i)
print 'A.__dict__.keys() :',A.__dict__.keys()

print '\na.i     ==',a.i
print 'id(a.i) ==',id(a.i)
print 'a.__dict__.keys() :',a.__dict__.keys()

a.add_i()

print '\nA.i     ==',A.i
print 'id(A.i) ==',id(A.i)
print 'A.__dict__.keys() :',A.__dict__.keys()

print '\na.i     ==',a.i
print 'id(a.i) ==',id(a.i)
print 'a.__dict__.keys() :',a.__dict__.keys()

结果

A.i     == 0
id(A.i) == 10021948
A.__dict__.keys() : ['i', 'add_i', '__module__', '__doc__']

a.i     == 0
id(a.i) == 10021948
a.__dict__.keys() : []

-----In add_i function-----
BEFORE: a.__dict__ : {}
BEFORE: id(self)   : 10021948

self.i = self.i + 1  done

AFTER: a.__dict__ : {'i': 1}
AFTER: id(self)   : 10021936
-----end of add_i function-----


A.i     == 0
id(A.i) == 10021948
A.__dict__.keys() : ['i', 'add_i', '__module__', '__doc__']

a.i     == 1
id(a.i) == 10021936
a.__dict__.keys() : ['i']

对象的__dict__公开对象的名称空间,即保持其属性的a mapping from names to objects

在创建了课程A及其实例a之后,只有A具有属性i
a没有。{ 事实上,此时a的命名空间是无效的,当解释器遇到a.i时,它会这样做:

  

一个类实例有一个名称实现为字典的名称空间   搜索属性引用的第一个位置。当一个   在那里找不到属性,并且实例的类有一个   通过该名称的属性,搜索继续该类   属性。见3 Data model 3.2 The standard type hierarchy
  在Python语言参考

也就是说,由于i不是a实例的属性,解释器会在A的命名空间中搜索:在那里找到属性i,它会将此属性作为a.i

调用的结果返回

现在,当执行a.add_i()时,指令self.i = self.i + 1的处理方式如下:

  • 首先搜索self.i:由于i不是self的属性(即a),因此返回的对象位于事实A.i

  • 其次,self.i + 1然后创建一个新对象,其值为A.i的递增值。但是现在这个对象与名称A.i的对象不同:它具有不同的身份,也就是说在内存中有不同的本地化。

  • 第三,有一个分配:self.i = .....
    这意味着名称i是在self(名称为a)的名称空间中创建的,并且绑定到刚刚创建的对象,其值增加。

因此,在add_i()函数的开头和结尾之间,self.i的含义发生了变化。
因此,a.i指令后a.add_i()的含义与之前的含义不再相同。

深入理解Python流程需要考虑标识符(名称)和对象在命名空间中播放的游戏,除此之外别无他法。

答案 2 :(得分:1)

因为您在类的实例上调用该方法,该实例具有自己的i副本。此代码可以执行您希望它执行的操作。

class A(object):
    i = 0
    def add_i(self):
        self.__class__.i += 1      

>> a = A()
>> a.add_i()
>> A.i
1

答案 3 :(得分:1)

存在变量i的副本。当您致电a.add_i()时,a.i会增加,而A.i会变为相同。

#a.i = 1, A.i = 1
a.add_i()
#a.i = 2, A.i = 2

想想如果你有

会发生什么
a = A()
b = A()
a.add_i()

print A.i()

答案 4 :(得分:0)

因为a.add_i()不是静态方法,它只能增加静态变量

答案 5 :(得分:0)

为了更改类级变量,您可以定义类级方法

In [15]: class A():
   ....:    i = 0
   ....:    @classmethod
   ....:    def incr(cls):
   ....:         cls.i += 1
   ....:         

In [16]: 

In [16]: A.incr()

In [17]: A.i
Out[17]: 1

In [18]: A().incr()

In [19]: A.i
Out[19]: 2

修改

或者在方法中引用类(而不是实例)

def incr(self):
    A.i += 1

如果类级属性是可变的,并且您想要更改它(不是赋值),例如对于列表,您可以采取任何方式

In [29]: class Class4Test(object):
    mylist = []
    @classmethod
    def add2list_class(cls, val):
        cls.mylist.append(val)
    def add2list_by_class(self, val):
        Class4Test.mylist.append(val)
    def add2list_by_inst(self,val):
        self.mylist.append(val)
   ....:         

In [30]: obj = Class4Test()

In [31]: obj.add2list_by_inst('val1')

In [32]: obj.add2list_by_class('val2')

In [33]: Class4Test.add2list_class('val3')

In [34]: obj.add2list_class('val4')

In [35]: Class4Test.mylist
Out[35]: ['val1', 'val2', 'val3', 'val4']