一、封裝與擴展
封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部調用者的代碼;而外部使用者只知道一個接口(函數),只要接口(函數)名、參數不變,使用者的代碼永遠無需改變。這就提供一個良好的合作基礎——或者說,只要接口這個基礎約定不變,則代碼改變不足為慮。
實例:
1 #類的設計者 2 class Room: 3 def __init__(self,name,owner,width,length,high): 4 self.name=name 5 self.owner=owner 6 self.__width=width #私有屬性,對外封閉,類的內部可以調用 7 self.__length=length 8 self.__high=high 9 10 def tell_area(self): #對外提供的接口,隱藏了內部的實現細節,此時我們想求的是面積 11 return self.__width * self.__length
1 #使用者 2 >>> r1=Room('卧室','egon',20,20,20) 3 >>> r1.tell_area() #使用者調用接口tell_area 4 400
1 #類的設計者,輕松的擴展了功能,而類的使用者完全不需要改變自己的代碼 2 class Room: 3 def __init__(self,name,owner,width,length,high): 4 self.name=name 5 self.owner=owner 6 self.__width=width 7 self.__length=length 8 self.__high=high 9 10 def tell_area(self): #對外提供的接口,隱藏內部實現,此時我們想求的是體積,內部邏輯變了,只需修改下列一行就可以很簡單的實現,而且外部調用感知不到,仍然使用該方法,但是功能已經變了 11 return self.__width * self.__length * self.__high
1 #對於仍然在使用tell_area接口的人來說,根本無需改動自己的代碼,就可以用上新功能 2 >>> r1.tell_area() 3 8000
二、靜態方法和類方法
通常情況下,在類中定義的所有函數(注意了,這里說的就是所有,跟self啥的沒關系,self也只是一個再普通不過的參數而已)都是對象的綁定方法,對象在調用綁定方法時會自動將自己作為參數傳遞給方法的第一個參數。除此之外還有兩種常見的方法:靜態方法和類方法,二者是為類量身定制的,但是實例非要使用,也不會報錯,后續將介紹。
1. 靜態方法
是一種普通函數,位於類定義的命名空間中,不會對任何實例類型進行操作,python為我們內置了函數staticmethod來把類中的函數定義成靜態方法
1 class Foo: 2 def spam(x,y,z): #類中的一個函數,千萬不要懵逼,self和x啥的沒有不同都是參數名 3 print(x,y,z) 4 spam=staticmethod(spam) #把spam函數做成靜態方法
基於之前所學裝飾器的知識,@staticmethod 等同於spam=staticmethod(spam),於是
1 class Foo: 2 @staticmethod #裝飾器 3 def spam(x,y,z): 4 print(x,y,z)
使用演示
1 print(type(Foo.spam)) #類型本質就是函數 2 Foo.spam(1,2,3) #調用函數應該有幾個參數就傳幾個參數 3 4 f1=Foo() 5 f1.spam(3,3,3) #實例也可以使用,但通常靜態方法都是給類用的,實例在使用時喪失了自動傳值的機制 6 7 ''' 8 <class 'function'> 9 1 2 3 10 3 3 3 11 '''
應用場景:編寫類時需要采用很多不同的方式來創建實例,而我們只有一個__init__函數,此時靜態方法就派上用場了
1 class Date: 2 def __init__(self,year,month,day): 3 self.year=year 4 self.month=month 5 self.day=day 6 7 @staticmethod 8 def now(): #用Date.now()的形式去產生實例,該實例用的是當前時間 9 t=time.localtime() #獲取結構化的時間格式 10 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建實例並且返回 11 12 @staticmethod 13 def tomorrow():#用Date.tomorrow()的形式去產生實例,該實例用的是明天的時間 14 t=time.localtime(time.time()+86400) 15 return Date(t.tm_year,t.tm_mon,t.tm_mday) 16 17 a=Date('1987',11,27) #自己定義時間 18 b=Date.now() #采用當前時間 19 c=Date.tomorrow() #采用明天的時間 20 21 print(a.year,a.month,a.day) 22 print(b.year,b.month,b.day) 23 print(c.year,c.month,c.day)
2. 類方法
類方法是給類用的,類在使用時會將類本身當做參數傳給類方法的第一個參數,python為我們內置了函數classmethod來把類中的函數定義成類方法
1 class A: 2 x=1 3 @classmethod 4 def test(cls): 5 print(cls,cls.x) 6 7 class B(A): 8 x=2 9 B.test() 10 11 ''' 12 輸出結果: 13 <class '__main__.B'> 2 14 '''
應用場景
1 import time 2 class Date: 3 def __init__(self,year,month,day): 4 self.year=year 5 self.month=month 6 self.day=day 7 8 @staticmethod 9 def now(): 10 t=time.localtime() 11 return Date(t.tm_year,t.tm_mon,t.tm_mday) 12 13 class EuroDate(Date): 14 def __str__(self): 15 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) 16 17 e=EuroDate.now() 18 19 print(e) #我們的意圖是想觸發EuroDate.__str__,但是結果為 20 ''' 21 輸出結果: 22 <__main__.Date object at 0x1013f9d68> 23 '''
因為e就是用父類Date產生的,所以根本不會觸發EuroDate.__str__,解決方法就是用classmethod
1 import time 2 class Date: 3 def __init__(self,year,month,day): 4 self.year=year 5 self.month=month 6 self.day=day 7 # @staticmethod 8 # def now(): 9 # t=time.localtime() 10 # return Date(t.tm_year,t.tm_mon,t.tm_mday) 11 12 @classmethod #改成類方法 13 def now(cls): 14 t=time.localtime() 15 return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪個類來調用,即用哪個類cls來實例化 16 17 class EuroDate(Date): 18 def __str__(self): 19 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) 20 21 e=EuroDate.now() 22 23 print(e) #我們的意圖是想觸發EuroDate.__str__,此時e就是由EuroDate產生的,所以會如我們所願 24 ''' 25 輸出結果: 26 year:2017 month:3 day:3 27 '''
強調,注意注意注意:靜態方法和類方法雖然是給類准備的,但是如果實例去用,也是可以用的,只不過實例去調用的時候容易讓人混淆,不知道你要干啥
1 x=e.now() #通過實例e去調用類方法也一樣可以使用,靜態方法也一樣 2 print(x) 3 ''' 4 輸出結果: 5 year:2017 month:3 day:3 6 '''
參考資料:
1. http://www.cnblogs.com/linhaifeng/articles/6182264.html#_label10