【轉】Python基礎-封裝與擴充套件、靜態方法和類方法
一、封裝與擴充套件
封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部呼叫者的程式碼;而外部使用者只知道一個介面(函式),只要介面(函式)名、引數不變,使用者的程式碼永遠無需改變。這就提供一個良好的合作基礎——或者說,只要介面這個基礎約定不變,則程式碼改變不足為慮。
例項:
1 #類的設計者 2 class Room: 3 def __init__(self,name,owner,width,length,high): 4 self.name=name 5self.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=length8 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