面向對象--類成員
聲明:內容轉至 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參數;執行類方法時,自動將調用該方法的類
- 靜態方法:由類調用;無默認參數;
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
廣泛應用在類的定義中,可以讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減少了出錯的可能性。
面向對象--類成員