1. 程式人生 > >面向對象--類成員

面向對象--類成員

圖片 註意 print mage set 同時 個學生 裝飾 span

聲明:內容轉至 https://www.cnblogs.com/z-joshua/p/6386596.html,謝謝共享!

類的成員可以分為三大類:字段、方法和屬性:

技術分享圖片

註:所有成員中,只有普通字段的內容保存對象中,即:根據此類創建了多少對象,在內存中就有多少個普通字段。而其他的成員,則都是保存在類中,即:無論對象的多少,在內存中只創建一份。

(一)字段

字段包括:普通字段和靜態字段,他們在定義和使用中有所區別,而最本質的區別是內存中保存的位置不同

技術分享圖片
 1 class Province:
 2     # 靜態字段
 3     country = "China"
 4 
 5     def __init__(self, name):
 6         # 普通字段
 7         self.name = name
 8 
 9 obj_henan = Province("HeNan")
10 
11 print(obj_hunan.name) # 使用對象訪問跑一趟那個字段
12 print(Province.country) # 使用類訪問靜態字段
技術分享圖片

由上述代碼可以看出【普通字段需要通過對象來訪問】【靜態字段通過類訪問】,在使用上可以看出普通字段和靜態字段的歸屬是不同的。其在內容的存儲方式類似如下圖:

技術分享圖片

由上圖可是:

  • 靜態字段在內存中只保存一份
  • 普通字段在每個對象中都要保存一份

應用場景: 通過類創建對象時,如果每個對象都具有相同的字段,那麽就使用靜態字段

(二)方法

方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不同。

  • 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self
  • 類方法:由調用; 至少一個cls參數;執行類方法時,自動將調用該方法的
    復制給cls
  • 靜態方法:由調用;無默認參數;
技術分享圖片
 1 class Foo:
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     # 普通方法,由對象調用
 6     def ord_fun(self):
 7         print(self.name)
 8 
 9     # 靜態方法
10     # 普通方法經過兩步變成靜態字段,1,去掉參數self,2,加上裝飾器 @staticmethod
11     # 如果類裏的方法用不到對象(self)裏的字段,靜態方法可以有除了self的參數
12     # 靜態方法其實就是面向過程裏的普通函數,之所以放到類裏就是表示方法和該類有關
13     @staticmethod
14     def static_func(arg1):
15         print(arg1)
16 
17     # 類方法
18     # 靜態方法的一種特殊形式,自帶特殊參數cls,使用裝飾器 @classmethod
19     @classmethod
20     def class_func(cls): # cls = class
21         print(cls)
22 
23 obj_foo = Foo("foo object")
24 obj_foo.ord_fun()
25 Foo.static_func("static func")
26 Foo.class_func()
技術分享圖片

技術分享圖片

相同點:對於所有的方法而言,均屬於類(非對象)中,所以,在內存中也只保存一份。

不同點:方法調用者不同、調用方法時自動傳入的參數不同。

(三) 屬性

為了理解Python的屬性,我們先來看一個例子,

假如我們有一個學生類:

1 class Student(object):
2     def __init__(self):
3         self.score = 0
4 
5 s1 = Student()
6 s1.score = 9999

這個賦值 s1.score = 9999 這顯然不合邏輯,score的正常取值範圍應該是0-100, 為了限制score的範圍,我們可能會通過定義一個set_score()方法來設置成績,再通過一個get_score()來獲取成績,這樣,在set_score()方法裏,就可以檢查參數:

技術分享圖片
 1 class Student(object):
 2     def __init__(self):
 3         pass 4 
 5     def set_score(self, value):
 6         if not isinstance(value, int):
 7             raise ValueError("Score must be int type")
 8         elif score < 0 or value> 100:
 9             raise ValueError("Score must between 0 - 100")
10         else:
11             self.score = value
12 
13 s1 = Student()
14 s1.set_score(9999)
技術分享圖片

這時 s1.set_score(9999) 就會有如下的報錯:

1     raise ValueError("Score must between 0 - 100")
2 ValueError: Score must between 0 - 100

但是,上面的調用方法又略顯復雜

有沒有既能檢查參數,又可以類似於訪問字段一樣設置score的方法呢?(使用score = 9999,而不用s1.set_score(9999)), 對於追求完美的Python程序員來說,這是必須要做到的!

還記得裝飾器(decorator)可以給函數動態加上功能嗎?對於類的方法,裝飾器一樣起作用。Python內置的@property裝飾器就是負責把一個方法變成屬性調用的:

於是我們對上面的Studeng類做修改:

技術分享圖片
 1 class Student(object):
 2     def __init__(self):
 3         pass
 4 
 5     @property
 6     def score(self, score):
 7         return self.score
 8 
 9     @score.setter
10     def score(self, value): # 函數名和@property一致
11         if not isinstance(value, int):
12             raise ValueError("Score must be int type")
13         elif value < 0 or value > 100:
14             raise ValueError("Score must between 0 - 100")
15         else:
16             self.score = value
17 
18 s1 = Student()
19 s1.score = 9999 # 這裏用 "="訪問score
技術分享圖片

@property的實現比較復雜,我們先考察如何使用。把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又創建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值,於是,我們就擁有一個可控的屬性操作:

1 raise ValueError("Score must between 0 - 100")
2 ValueError: Score must between 0 - 100

註意到這個神奇的@property,我們在對實例屬性操作的時候,就知道該屬性很可能不是直接暴露的,而是通過getter和setter方法來實現的。

還可以定義只讀屬性,只定義getter方法,不定義setter方法就是一個只讀屬性:

技術分享圖片
 1     @property
 2     def birth(self):
 3         return self._birth
 4 
 5     @birth.setter
 6     def birth(self, value):
 7         self._birth = value
 8 
 9     @property
10     def age(self):
11         return 2017 - self._birth
技術分享圖片

上面的birth是可讀寫屬性,而age就是一個只讀屬性,因為age可以根據birth和當前時間計算出來。

@property廣泛應用在類的定義中,可以讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減少了出錯的可能性。

面向對象--類成員