一、特性(property)
1 什么是特性property
property是一種特殊的屬性,訪問它時會執行一段功能(函數)然后返回值
2 class Circle: 3 def __init__(self,radius): #圓的半徑radius 4 self.radius=radius 5 6 @property 7 def area(self): 8 return math.pi * self.radius**2 #計算面積 9 10 @property 11 def perimeter(self): 12 return 2*math.pi*self.radius #計算周長 13 14 c=Circle(10) 15 print(c.radius) 16 print(c.area) #可以向訪問數據屬性一樣去訪問area,會觸發一個函數的執行,動態計算出一個值 17 print(c.perimeter) #同上 18 ''' 19 輸出結果: 20 314.1592653589793 21 62.83185307179586 22 '''
注意:此時的特性arear和perimeter不能被賦值
c.area=3 #為特性area賦值 ''' 拋出異常: AttributeError: can't set attribute '''
2 為什么要用property
將一個類的函數定義成特性以后,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然后計算出來的,這種特性的使用方式遵循了統一訪問的原則
除此之外,看下
ps:面向對象的封裝有三種方式: 【public】 這種其實就是不封裝,是對外公開的 【protected】 這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是“兒子”,但我不知道為什么大家 不說“女兒”,就像“parent”本來是“父母”的意思,但中文都是叫“父類”)公開 【private】 這種封裝對誰都不公開
python並沒有在語法上把它們三個內建到自己的class機制中,在C++里一般會將所有的所有的數據都設置為私有的,然后提供set和get方法(接口)去設置和獲取,在python中通過property方法可以實現
1 class Foo: 2 def __init__(self,val): 3 self.__NAME=val #將所有的數據屬性都隱藏起來 4 5 @property 6 def name(self): 7 return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置) 8 9 @name.setter 10 def name(self,value): 11 if not isinstance(value,str): #在設定值之前進行類型檢查 12 raise TypeError('%s must be str' %value) 13 self.__NAME=value #通過類型檢查后,將值value存放到真實的位置self.__NAME 14 15 @name.deleter 16 def name(self): 17 raise TypeError('Can not delete') 18 19 f=Foo('egon') 20 print(f.name) 21 # f.name=10 #拋出異常'TypeError: 10 must be str' 22 del f.name #拋出異常'TypeError: Can not delete'

1 class Foo: 2 def __init__(self,val): 3 self.__NAME=val #將所有的數據屬性都隱藏起來 4 5 def getname(self): 6 return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置) 7 8 def setname(self,value): 9 if not isinstance(value,str): #在設定值之前進行類型檢查 10 raise TypeError('%s must be str' %value) 11 self.__NAME=value #通過類型檢查后,將值value存放到真實的位置self.__NAME 12 13 def delname(self): 14 raise TypeError('Can not delete') 15 16 name=property(getname,setname,delname) #不如裝飾器的方式清晰 17 18 一種property的古老用法
二、靜態方法(staticmethod)
通常情況下,在類中定義的所有函數(注意了,這里說的就是所有,跟self啥的沒關系,self也只是一個再普通不過的參數而已)都是對象的綁定方法,對象在調用綁定方法時會自動將自己作為參數傳遞給方法的第一個參數。除此之外還有兩種常見的方法:靜態方法和類方法,二者是為類量身定制的,但是實例非要使用,也不會報錯。
是一種普通函數,位於類定義的命名空間中,不會對任何實例類型進行操作,python為我們內置了函數staticmethod來把類中的函數定義成靜態方法
class Foo: def spam(x,y,z): #類中的一個函數,千萬不要懵逼,self和x啥的沒有不同都是參數名 print(x,y,z) spam=staticmethod(spam) #把spam函數做成靜態方法
基於之前所學裝飾器的知識,@staticmethod 等同於spam=staticmethod(spam),於是
class Foo: @staticmethod #裝飾器 def spam(x,y,z): print(x,y,z)
使用演示
print(type(Foo.spam)) #類型本質就是函數 Foo.spam(1,2,3) #調用函數應該有幾個參數就傳幾個參數 f1=Foo() f1.spam(3,3,3) #實例也可以使用,但通常靜態方法都是給類用的,實例在使用時喪失了自動傳值的機制 ''' <class 'function'> 2 3 3 3 '''
應用場景:編寫類時需要采用很多不同的方式來創建實例,而我們只有一個__init__函數,此時靜態方法就派上用場了
class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去產生實例,該實例用的是當前時間 t=time.localtime() #獲取結構化的時間格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建實例並且返回 @staticmethod def tomorrow():#用Date.tomorrow()的形式去產生實例,該實例用的是明天的時間 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) a=Date('1987',11,27) #自己定義時間 b=Date.now() #采用當前時間 c=Date.tomorrow() #采用明天的時間 print(a.year,a.month,a.day) print(b.year,b.month,b.day) print(c.year,c.month,c.day)
三、類方法(classmethod)
類方法是給類用的,類在使用時會將類本身當做參數傳給類方法的第一個參數,python為我們內置了函數classmethod來把類中的函數定義成類方法
class A: x=1 @classmethod def test(cls): print(cls,cls.x) class B(A): x=2 B.test() ''' 輸出結果: <class '__main__.B'> 2 '''
應用場景:
import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): t=time.localtime() return Date(t.tm_year,t.tm_mon,t.tm_mday) class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我們的意圖是想觸發EuroDate.__str__,但是結果為 ''' 輸出結果: <__main__.Date object at 0x1013f9d68> '''
因為e就是用Date類產生的,所以根本不會觸發EuroDate.__str__,解決方法就是用classmethod
import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day # @staticmethod # def now(): # t=time.localtime() # return Date(t.tm_year,t.tm_mon,t.tm_mday) @classmethod #改成類方法 def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪個類來調用,即用哪個類cls來實例化 class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我們的意圖是想觸發EuroDate.__str__,此時e就是由EuroDate產生的,所以會如我們所願 ''' 輸出結果: year:2017 month:3 day:3 '''
強調,注意注意注意:靜態方法和類方法雖然是給類准備的,但是如果實例去用,也是可以用的,只不過實例去調用的時候容易讓人混淆,不知道你要干啥
x=e.now() #通過實例e去調用類方法也一樣可以使用,靜態方法也一樣 print(x) ''' 輸出結果: year:2017 month:3 day:3 '''
四、附加知識點__str__的用法
#__str__定義在類內部,必須返回一個字符串類型, #什么時候會出發它的執行呢?打印由這個類產生的對象時,會觸發執行 class People: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return '<name:%s,age:%s>' %(self.name,self.age) p1=People('egon',18) print(p1) str(p1) #----->p1.__str__()