簡體   English   中英

從Python中的實例方法更改靜態變量

[英]Changing static variable from instance method in 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() ,為什么它不會遞增'static'變量i

當您分配給self.i ,您正在創建一個名為i的新實例變量:

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

以下將改變Ai而不是重新綁定ai

A.i = A.i + 1

或者,更短:

A.i += 1

以下代碼准確顯示了發生的情況:

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類及其實例a ,只有A具有屬性i
a沒有。
其實在這一刻,的命名空間a是無效的,而當遇到intepreter ai它是:

類實例具有作為字典實現的名稱空間,該字典是搜索屬性引用的第一個位置。 當在那里找不到屬性,並且實例的類具有該名稱的屬性時,搜索繼續使用類屬性。 參見3數據模型3.2標准類型層次結構
在Python語言參考中

也就是說,因為i不是a實例的屬性,解釋器去搜索A的命名空間:它找到一個屬性i並且它返回該屬性作為ai調用的結果

現在,當執行a.add_i()時,指令self.i = self.i + 1的處理方式如下:

  • 首先,搜索self.i :因為i不是self (屬於a )的屬性,所以返回的對象實際上是Ai

  • 其次, self.i + 1然后創建一個新對象,其值是Ai的遞增值。 但是現在這個對象與名字Ai的對象不同:它具有不同的身份,也就是說在內存中有不同的本地化。

  • 第三,有一個分配: self.i = .....
    這意味着名稱i是在self (即a )的命名空間中創建的,並且綁定到剛剛創建的具有遞增值的對象。

所以在add_i()函數的開頭和它的結尾之間, self.i的含義已經改變了。
因此,指令a.add_i()之后的ai的含義與之前不同。

深入理解Python進程需要考慮標識符 (名稱)和對象在命名空間中播放的游戲,除此之外別無他法。

因為您正在類的實例上調用該方法,該實例具有它自己的i副本。 此代碼可以執行您希望它執行的操作。

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

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

存在變量i副本。 當你調用a.add_i()ai增加,而Ai會變為相同。

#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()

因為a.add_i()不是靜態方法,它只能增加靜態變量

要更改類級變量,可以定義類級方法

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM