1 什麼是特性propertyhtml
property是一種特殊的屬性,訪問它時會執行一段功能(函數)而後返回值python
1 import math 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 爲何要用propertyspa
將一個類的函數定義成特性之後,對象再去使用的時候obj.name,根本沒法察覺本身的name是執行了一個函數而後計算出來的,這種特性的使用方式遵循了統一訪問的原則3d
除此以外,看下code
ps:面向對象的封裝有三種方式: 【public】 這種其實就是不封裝,是對外公開的 【protected】 這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是「兒子」,但我不知道爲何你們 不說「女兒」,就像「parent」原本是「父母」的意思,但中文都是叫「父類」)公開 【private】 這種封裝對誰都不公開
python並無在語法上把它們三個內建到本身的class機制中,在C++裏通常會將全部的全部的數據都設置爲私有的,而後提供set和get方法(接口)去設置和獲取,在python中經過property方法能夠實現htm
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'
一般狀況下,在類中定義的全部函數(注意了,這裏說的就是全部,跟self啥的不要緊,self也只是一個再普通不過的參數而已)都是對象的綁定方法,對象在調用綁定方法時會自動將本身做爲參數傳遞給方法的第一個參數。除此以外還有兩種常見的方法:靜態方法和類方法,兩者是爲類量身定製的,可是實例非要使用,也不會報錯。對象
是一種普通函數,位於類定義的命名空間中,不會對任何實例類型進行操做,python爲咱們內置了函數staticmethod來把類中的函數定義成靜態方法blog
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)
類方法是給類用的,類在使用時會將類自己當作參數傳給類方法的第一個參數,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__定義在類內部,必須返回一個字符串類型, #何時會出發它的執行呢?打印由這個類產生的對象時,會觸發執行 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__()