python的類和對象——類的靜態字段番外篇


什么是靜態字段

  在開始之前,先上圖,解釋一下什么是類的靜態字段(我有的時候會叫它類的靜態變量,總之說的都是它。后面大多數情況可能會簡稱為類變量。):

  

  我們看上面的例子,這里的money就是靜態字段,首先看它的位置,是在father類中,而不是在__init__中。那么一個小小的靜態字段,我為什么要特意寫一篇番外給它呢?耐着性子看下去,你就會發現一個小小的類變量,卻折射出了整個類的世界。

  首先我們先來解釋一下什么叫做靜態字段:

        

  我們看上面的例子,左中右三張圖,左邊是純凈的代碼,中間是我給代碼加上的內存加載過程,右邊是執行結果。我們在這里先看中間的圖,來看這個文件加載的過程。

  1.將類存入了內存 2.將money變量放入了內存 3.將__init__方法的地址放入了內存

  接下來我們執行了一個__dict__方法,我們看右邊圖中現實的執行結果,發現這個時候內存中已經存入了money這個變量,它隨着這個程序的執行產生,隨着程序的結束而消失,這樣和程序‘共存亡’的字段,我們就叫它靜態字段。它就像是一個全局變量,不屬於任何一個對象,我們可以直接使用類來調用,也可以在對象使用方法的時候使用它。它是對象共享的變量,存在類的內存里。

靜態字段的調用方法

  剛剛我們知道了什么是靜態字段,現在我們就來看看靜態字段是怎么使用的?

       

  上面給出了兩種使用方式,類調用和對象調用,哎?看起來好像可以哎!我們再來修改一下類變量瞧瞧:

       

  我們看,當我們使用 類名.靜態字段名 修改類變量之后,使用類或者對象去調用這個靜態字段,發現它們都產生了變化。好像一切都在我們的預料之中哎,這樣的話對象和類都可以使用類變量的樣子!如果你們真的信了那就太天真了。。。看看下面這個例子:

        

  看上面的圖,我是接着上面的例子寫的,黃框框里是我加上的內容,我們結果中最后打印出來的內容,哎?我們看到了什么?好像對象調用的類變量只是針對各自的對象發生了改變,並沒有改變類中money變量的值,說好的全局變量呢?這個現象是怎么發生的呢?我們不防來推理一下:

        

  看上面兩張圖,左邊是正常的邏輯,當我們類的內存中有一個靜態字段的時候,我們使用類去調用這個字段,自然找到的是靜態字段,當我們使用對象去調用的時候,這個對象指針先在自己的內存里找了找,發現沒找到,於是就用對象中維護的類指針到類的內存中去找,果然找到了money,於是歡歡喜喜的打印了出來,我們也如願以償的看到了想要的結果。當我們使用 類 去調用這個靜態字段進行修改的時候,我們修改的是 類 的內存中維護的money字段,所以並沒有影響上述過程。

  再看右邊這張圖,當我們使用對象去調用並改變一個類的靜態字段的時候,它們在自己的內存中並沒有money字段,所以還是通過類指針到類內存中去找,但是當它們找到之后,就會在自己的內存空間開辟一塊空間來存儲對這個靜態字段修改后的結果。所以,這個時候類中的靜態字段就不會被改變,而兩個對象中的money字段也就不會互相影響了。

       

  這個時候我們在剛剛的基礎上再加上一段代碼,來看執行的結果,我們發現這個時候再使用類變量對類的靜態變量進行修改,分別看看類和對象中這個變量的變化,我們發現對象中的變量沒有按照我們期待的那樣發生改變,這也驗證了我們的猜想,因為這個時候對象的內存中已經有了money變量,它們就不願意舍近求遠的到類內存中去取變量來給我們顯示了。

 1 #!/usr/bin/env python
 2 #-*-coding:utf-8-*-
 3 __author__ = 'Eva_J'
 4 class father(object):
 5 
 6     #靜態字段
 7     money = 10000
 8     def __init__(self,name):
 9 
10         #普通字段
11         self.name = name
12 
13 #類的實例化,分別實例化出了兩個對象father_obj1,father_obj2
14 father_obj1 = father('obj1')
15 father_obj2 = father('obj2')
16 
17 #類調用
18 print 'father.money:',father.money
19 #對象調用
20 print 'father_obj1.money:',father_obj1.money
21 print 'father_obj2.money:',father_obj2.money
22 
23 #使用類調用修改
24 father.money = father.money + 1000
25 print 'father.money:',father.money
26 print 'father_obj1.money:',father_obj1.money
27 print 'father_obj2.money:',father_obj2.money
28 
29 #使用對象調用修改
30 father_obj1.money = father_obj1.money + 1
31 father_obj2.money = father_obj2.money + 2
32 print 'father.money:',father.money
33 print 'father_obj1.money:',father_obj1.money
34 print 'father_obj2.money:',father_obj2.money
35 
36 father.money = father.money + 66
37 print 'father.money:',father.money
38 print 'father_obj1.money:',father_obj1.money
39 print 'father_obj2.money:',father_obj2.money
demo Code

  但是,我們變量類型換乘字典試試看,結果又不一樣了,代碼在下面,自己粘回去執行吧,這里就不給你們貼花花綠綠的圖了:

    

 1 #!/usr/bin/env python
 2 #-*-coding:utf-8-*-
 3 __author__ = 'Eva_J'
 4 class father(object):
 5 
 6     #靜態字段
 7     money = {'money':10000}
 8     def __init__(self,name):
 9 
10         #普通字段
11         self.name = name
12 
13 #類的實例化,分別實例化出了兩個對象father_obj1,father_obj2
14 father_obj1 = father('obj1')
15 father_obj2 = father('obj2')
16 #類調用
17 print 'father.money:',father.money['money']
18 #對象調用
19 print 'father_obj1.money:',father_obj1.money['money']
20 print 'father_obj2.money:',father_obj2.money['money']
21 
22 #使用類調用修改
23 father.money['money'] = father.money['money'] + 1000
24 print 'father.money:',father.money['money']
25 print 'father_obj1.money:',father_obj1.money['money']
26 print 'father_obj2.money:',father_obj2.money['money']
27 
28 #使用對象調用修改
29 father_obj1.money['money'] = father_obj1.money['money'] + 1
30 father_obj2.money['money'] = father_obj2.money['money'] + 2
31 print 'father.money:',father.money['money']
32 print 'father_obj1.money:',father_obj1.money['money']
33 print 'father_obj2.money:',father_obj2.money['money']
34 
35 father.money['money'] = father.money['money'] + 66
36 print 'father.money:',father.money['money']
37 print 'father_obj1.money:',father_obj1.money['money']
38 print 'father_obj2.money:',father_obj2.money['money']
demo Code

  為什么?這就和不同數據類型維護的指針有關系了。在這里不詳細的贅述。

  偷懶的同學看這里→_→ :我們只需要記住,在使用類的靜態變量的時候,必須要用類名來調用和修改。它才會永遠被類和對象共享。

從類的繼承這個角度來看看靜態字段

  剛剛我們已經知道了對象和類中靜態字段的存儲及調用過程,下面我們就從類的繼承這個角度來看看靜態字段:

 1 #!/usr/bin/env python
 2 #-*-coding:utf-8-*-
 3 __author__ = 'Eva_J'
 4 class father(object):
 5 
 6     #靜態字段
 7     money = 10000
 8     def __init__(self,name):
 9 
10         #普通字段
11         self.name = name
12 
13 class Son1(father):
14     pass
15 
16 class Son2(father):
17     pass
18 
19 class GrandSon(Son1,Son2):
20     pass
21 
22 print 'father.money : ',father.money
23 print 'Son1.money : ',Son1.money
24 print 'Son2.money : ',Son2.money
25 print 'GrandSon.money : ',GrandSon.money
26 print '*'*25
27 father.money += 1000
28 print 'father.money : ',father.money
29 print 'Son1.money : ',Son1.money
30 print 'Son2.money : ',Son2.money
31 print 'GrandSon.money : ',GrandSon.money
32 print '*'*25
33 Son1.money += 100
34 Son2.money += 200
35 print 'father.money : ',father.money
36 print 'Son1.money : ',Son1.money
37 print 'Son2.money : ',Son2.money
38 print 'GrandSon.money : ',GrandSon.money
39 print '*'*25
40 GrandSon.money += 1
41 print 'father.money : ',father.money
42 print 'Son1.money : ',Son1.money
43 print 'Son2.money : ',Son2.money
44 print 'GrandSon.money : ',GrandSon.money
45 print '*'*25
46 father.money += 2000
47 print 'father.money : ',father.money
48 print 'Son1.money : ',Son1.money
49 print 'Son2.money : ',Son2.money
50 print 'GrandSon.money : ',GrandSon.money
demoCode

  上面這段代碼的執行結果是這樣的:

   

  原理在下面,當我們使用創建類的時候,每個基類和派生類都會產生自己的內存,一開始派生類中沒有money變量,所以在調用的時候它們通過類中維護的指針都順利地找到了父類中的money變量,返回給了我們,但是當我們使用 派生類名.靜態字段名 對派生類中的靜態字段進行修改的時候,它們就默默地把修改的結果存在了自己的內存空間內。所以在之后的調用中就不千里迢迢的去父類中找這個變量了。其實和上面的道理是一樣一樣噠!

 

  偷懶的同學看這里→_→ :我們只需要記住,在使用類的靜態變量的時候,如果我們希望基類和各派生類的靜態字段被共享,必須要用基類名來調用和修改。  

  到這里關於類的靜態字段的內容就全部講完了,簡簡單單的一個靜態字段,竟然囊括了這么多知識點,是不是值得我們花一點點時間來搞清楚呢?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM