類的裝飾器,繫結方法與非繫結方法,繼承,繼承背景下的屬性查詢
一、類的裝飾器
BMI指數是用來衡量一個人的體重與身高對健康影響的一個指標,計算公式為
成人的BMI數值:
過輕:低於18.5
正常:18.5-23.9
過重:24-27
肥胖:28-32
非常肥胖, 高於32
體質指數(BMI)=體重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
>>> class People: ... def __init__(self,name,weight,height): ... self.name=name ... self.weight=weight ... self.height=height ... @property ... def bmi(self): ... return self.weight / (self.height**2) ... >>> obj=People('lili',75,1.85) >>> obj.bmi #觸發方法bmi的執行,將obj自動傳給self,執行後返回值作為本次引用的結果 21.913805697589478
使用property有效地保證了屬性訪問的一致性。另外property還提供設定和刪除屬性的功能,如下
>>> class Foo: ... def __init__(self,val): ... self.__NAME=val #將屬性隱藏起來 ... @property ... def name(self): ... return self.__NAME ... @name.setter ... def name(self,value): ... if not isinstance(value,str): #在設定值之前進行型別檢查 ... raise TypeError('%s must be str' %value) ... self.__NAME=value #通過型別檢查後,將值value存放到真實的位置self.__NAME ... @name.deleter ... def name(self): ... raise PermissionError('Can not delete') ... >>> f=Foo('lili') >>> f.name lili >>> f.name='LiLi' #觸發name.setter裝飾器對應的函式name(f,’Egon') >>> f.name=123 #觸發name.setter對應的的函式name(f,123),丟擲異常TypeError >>> del f.name #觸發name.deleter對應的函式name(f),丟擲異常PermissionError
二、繫結方法與非繫結方法
類中定義的函式分為兩大類:繫結方法和非繫結方法
其中繫結方法又分為繫結到物件的物件方法和繫結到類的類方法。
在類中正常定義的函式預設是繫結到物件的,而為某個函式加上裝飾器@classmethod後,該函式就繫結到了類。
繫結方法的特點:繫結給誰就應該由誰來呼叫,誰來呼叫就會將自己當做第一個引數傳入
為類中某個函式加上裝飾器@staticmethod後,該函式就變成了非繫結方法,也稱為靜態方法。該方法不與類或物件繫結,類與物件都可以來呼叫它,但它就是一個普通函式而已,因而沒有自動傳值那麼一說
非繫結方法的特點:不與類和物件繫結,意味著誰都可以來呼叫,但無論誰來呼叫就是一個普通函式,沒有自動傳參的效果
class People:
def __init__(self,name):
self.name = name
# 但凡在類中定義一個函式,預設就是繫結給物件的,應該由物件來呼叫,
# 會將物件當作第一個引數自動傳入
def tell(self):
print(self.name)
# 類中定義的函式被classmethod裝飾過,就繫結給類,應該由類來呼叫,
# 類來呼叫會類本身當作第一個引數自動傳入
@classmethod
def f1(cls): # cls = People
print(cls)
# 類中定義的函式被staticmethod裝飾過,就成一個非繫結的方法即一個普通函式,誰都可以呼叫,
# 但無論誰來呼叫就是一個普通函式,沒有自動傳參的效果
@staticmethod
def f2(x,y):
print(x,y)
p1 = People('egon')
# p1.tell()
# print(People.f1)
# People.f1()
# print(People.f2)
# print(p1.f2)
# People.f2(1,2)
# p1.f2(3,4)
三、繼承
繼承是建立新類的一種方式
新建的類稱之為子類, 被繼承的類稱之為父類、基類、超類
繼承的特點是:子類可以遺傳父類的屬性
類是用解決物件之間冗餘問題的, 而繼承則是來解決類與類之間冗餘問題的
在python中支援多繼承
class ParentClass1: #定義父類
pass
class ParentClass2: #定義父類
pass
class SubClass1(ParentClass1): #單繼承
pass
class SubClass2(ParentClass1,ParentClass2): #多繼承
pass
通過類的內建屬性__bases__可以檢視類繼承的所有父類
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
在Python2中有經典類與新式類之分,沒有顯式地繼承object類的類,以及該類的子類,都是經典類,顯式地繼承object的類,以及該類的子類,都是新式類。而在Python3中,即使沒有顯式地繼承object,也會預設繼承該類,在python3中全都是新式類。如下
>>> ParentClass1.__bases__
(<class ‘object'>,)
>>> ParentClass2.__bases__
(<class 'object'>,)
繼承與抽象(先抽象再繼承)
繼承描述的是子類與父類之間的關係,是一種什麼是什麼的關係。要找出這種關係,必須先抽象再繼承
抽象即抽取類似或者說比較像的部分。
繼承:是基於抽象的結果,通過程式語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。
四、繼承背景下的屬性查詢
有了繼承關係,物件在查詢屬性時,先從物件自己的--dict--中找,如果沒有則去子類中找,然後再去父類中找……
>>> class Foo:
... def f1(self):
... print('Foo.f1')
... def f2(self):
... print('Foo.f2')
... self.f1()
...
>>> class Bar(Foo):
... def f1(self):
... print('Foo.f1')
...
>>> b=Bar()
>>> b.f2()
Foo.f2
Foo.f1
b.f2()會在父類Foo中找到f2,先列印Foo.f2,然後執行到self.f1(),即b.f1(),仍會按照:物件本身->類Bar->父類Foo的順序依次找下去,在類Bar中找到f1,因而列印結果為Foo.f1
父類如果不想讓子類覆蓋自己的方法,可以採用雙下劃線開頭的方式將方法設定為私有的
>>> class Foo:
... def __f1(self): # 變形為_Foo__fa
... print('Foo.f1')
... def f2(self):
... print('Foo.f2')
... self.__f1() # 變形為self._Foo__fa,因而只會呼叫自己所在的類中的方法
...
>>> class Bar(Foo):
... def __f1(self): # 變形為_Bar__f1
... print('Foo.f1')
...
>>>
>>> b=Bar()
>>> b.f2() #在父類中找到f2方法,進而呼叫b._Foo__f1()方法,同樣是在父類中找到該方法
Foo.f2
Foo.f1