標籤:成績 複製 修改 init 檢查 相同 try python __init__
類的成員可以分為三大類:欄位、方法和屬性:
註:所有成員中,只有普通欄位的內容儲存對象中,即:根據此類建立了多少對象,在記憶體中就有多少個普通欄位。而其他的成員,則都是儲存在類中,即:無論對象的多少,在記憶體中只建立一份。
(一)欄位
欄位包括:普通欄位和靜態欄位,他們在定義和使用中有所區別,而最本質的區別是記憶體中儲存的位置不同
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,加上裝飾器 @staticmethod11 # 如果類裡的方法用不到對象(self)裡的欄位,靜態方法可以有除了self的參數12 # 靜態方法其實就是面向過程裡的普通函數,之所以放到類裡就是表示方法和該類有關13 @staticmethod14 def static_func(arg1):15 print(arg1)16 17 # 類方法18 # 靜態方法的一種特殊形式,內建特殊參數cls,使用裝飾器 @classmethod19 @classmethod20 def class_func(cls): # cls = class21 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 = 04 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 = value12 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.setter10 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 = value17 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 @property10 def age(self):11 return 2017 - self._birth
上面的birth是可讀寫屬性,而age就是一個唯讀屬性,因為age可以根據birth和目前時間計算出來。
@property廣泛應用在類的定義中,可以讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程式運行時就減少了出錯的可能性。
Python物件導向-類成員