標籤:工作 分享圖片 abc 優點 字串 自己 深度 ssr 根據
標籤(空格分隔): 繼承
什麼是繼承:
繼承是指類與類之間的關係,是一種什麼“是”什麼的關係,繼承的功能之一就是用來解決代買重用問題,繼承是一種建立新類的方式,在python中,建立的類是可以繼承多個父類,父類又可以成為基類或者超類,建立的類稱為衍生類別或者子類;
派生
派生:是在繼承中,子類有自己的特點,有自己的東西,即使是同一個方法,子類也可以有自己的特色;
繼承的實現原理:
繼承的原則:
深度優先,廣度優先;
子類繼承後,對方法重寫
如果子類中有的方法,並且重寫了,在執行個體化的時候,還是先用子類的方法,如果子類找不到就用,繼承來的;
例如:選課系統:
選課系統裡面:有老師,學生,這些可以定義為類;
class Teacher: school ='luffycity' def __init__(self,name,age,sex,level,salary): self.name=name self.age=age self.sex=sex self.level=level self.salary=salary def teach(self): print('%s is teaching '%self.name)class Student: school ='luffycity' def __init__(self,name,age,sex,class_time): self.name=name self.age=age self.sex=sex self.class_time=class_time def learn(self): print(%s is learning '%self.name)
上述代碼會發現我們這裡面有很多的重複代碼,會有很多的重複的代碼,所以針對類之間的重複的代碼,我們可以使用繼承,把相同的功能,封裝,然後繼承,如上述我們可以,定義一個people,然後老師和學生繼承就好了;
so:如下代碼;
class People: school = 'luffycity' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Teacher(People): def __init__(self,name,age,sex,level,salary): super().__init__(name,age,sex) self.level=level self.salary=salary def teach(self): print('%s is teaching '%self.name)class Student: school ='luffycity' def __init__(self,name,age,sex,class_time): super().__init__(name,age,sex) self.class_time=class_time def learn(self): print(%s is learning '%self.name) teacher1 = Teacher('www',18,'male',10,3000)student1=Student('zhangsan',28,'feamal','18:34:45')
如上的代碼如果:老師有課程,課程周期,課程價格的屬性的時候,怎麼辦呢,代碼如下:我們可以給__init__增加屬性
class People: school = 'luffycity' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Teacher(People): def __init__(self,name,age,sex,level,salary,course_name,course_price,course_period): super().__init__(name,age,sex) self.level=level self.salary=salary self.course_name=course_name self.course_price=course_price self.course_period=course_period def teach(self): print('%s is teaching '%self.name)class Student: school ='luffycity' def __init__(self,name,age,sex,class_time): super().__init__(name,age,sex) self.class_time=class_time def learn(self): print(%s is learning '%self.name) teacher1 = Teacher('www',18,'male',10,3000,'python',3000,'dkkd')teacher1 = Teacher('aaa',18,'male',10,3000,'python',3000,'dkkd')
如上述的代碼會有很多,關於課程的資訊,寫了很多重複的代碼;
這樣我們可以把課程單獨的定義一個類:
class People: school = 'luffycity' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Teacher(People): def __init__(self,name,age,sex,level,salary,): super().__init__(name,age,sex) self.level=level self.salary=salary def teach(self): print('%s is teaching '%self.name)class Student: school ='luffycity' def __init__(self,name,age,sex,class_time): super().__init__(name,age,sex) self.class_time=class_time def learn(self): print(%s is learning '%self.name) class Course: def __init__(self,course_name,course_price,course_period) self.course_name =course_name self.course_price=course_price self.course_period =course_period def tell_info(self): print('課程名稱<%s> 課程價格<%s> 可傳給你周期<%s>'%(self.course_name,self.course_price,self.course))teacher1.course.tell_info()
這就是組合
teacher1 =Teacher(‘alex‘,18,‘male‘,10,3000,)
teacher1.course=‘python‘
teacher2.course=‘python‘
```;
多態
多態指的是一類事物有多種形態,比如動物有多種形態:人,狗,豬
import abcclass Animal(metaclass=abc.ABCMeta): #同一類事物:動物 @abc.abstractmethod def talk(self): passclass People(Animal): #動物的形態之一:人 def talk(self): print('say hello')class Dog(Animal): #動物的形態之二:狗 def talk(self): print('say wangwang')class Pig(Animal): #動物的形態之三:豬 def talk(self): print('say aoao')
研究多態,就是研究同一種事物多種狀態,
多態性:指的是可以在不考慮執行個體的類型的情況下,而直接使用對象,
peo1=People()
dog1=Dog()
pig1=Pig()
peo1.talk()
dog1.talk()
pig1.talk()
以上的多態性,指的是動態多態性,多態性,不用考慮具體的執行個體類型,就可以直接用;
def func(animal): animal.talk()func(peo1)func(pig1)func(dog1)
二 為什麼要用多態性(多態性的好處)
其實大家從上面多態性的例子可以看出,我們並沒有增加什麼新的知識,也就是說python本身就是支援多態性的,這麼做的好處是什麼呢?
1.增加了程式的靈活性
以不變應萬變,不論對象千變萬化,使用者都是同一種形式去調用,如func(animal)
2.增加了程式額可擴充性
通過繼承animal類建立了一個新的類,使用者無需更改自己的代碼,還是用func(animal)去調用
Python崇尚鴨子類型,即‘如果看起來像、叫聲像而且走起路來像鴨子,那麼它就是鴨子’
python程式員通常根據這種行為來編寫程式。例如,如果想編寫現有對象的自訂版本,可以繼承該對象
也可以建立一個外觀和行為像,但與它無任何關係的全新對象,後者通常用於儲存程式組件的松耦合度。
例1:利用標準庫中定義的各種‘與檔案類似’的對象,儘管這些對象的工作方式像檔案,但他們沒有繼承內建檔案對象的方法
#二者都像鴨子,二者看起來都像檔案,因而就可以當檔案一樣去用class TxtFile: def read(self): pass def write(self): passclass DiskFile: def read(self): pass def write(self): pass
例2:序列類型有多種形態:字串,列表,元組,但他們直接沒有直接的繼承關係
#str,list,tuple都是序列類型s=str('hello')l=list([1,2,3])t=tuple((4,5,6))#我們可以在不考慮三者類型的前提下使用s,l,ts.__len__()l.__len__()t.__len__()len(s)len(l)len(t)
封裝
從封裝本身的意思去理解,封裝就好像是拿來一個麻袋,把小貓,小狗,小王八,還有alex一起裝進麻袋,然後把麻袋封上口子。照這種邏輯看,封裝=‘隱藏’,這種理解是相當片面的
如何?隱藏:
- 在python中用雙底線開頭的方式將屬性隱藏起來(設定成私人的)
class A: __N=0 #類的資料屬性就應該是共用的,但是文法上是可以把類的資料屬性設定成私人的如__N,會變形為_A__N def __init__(self): self.__X=10 #變形為self._A__X def __foo(self): #變形為_A__foo print('from A') def bar(self): self.__foo() print('from bar') #只有在類內部才可以通過__foo的形式訪問到.#A._A__N是可以訪問到的,即這種操作並不是嚴格意義上的限制外部存取,僅僅只是一種文法意義上的變形
注意:以上的__N是隱藏的屬性,注意python裡面,以__開頭的是隱藏,以__開頭,__結尾是python內建的屬性和方法,這點大家切記!,不要搞混了;
變形的特點:
1.外部無法直接存取obj.__attrName
2.在類的內部可不可以obj.__AttriName,答案是可以的
3.子類無法覆蓋父類__開頭的屬性;(因為他兩個不是一個名字)
封裝的意義
1.封裝資料屬性:明確的區分內外
class People: def __init__(self,name,age): self.__name =name self.__age=agep=People('egon',18)P.__name
資料屬性分裝之後外部是不可以訪問的,在內部是可以,但是外部可以間接的訪問,我們可以在類的內部開一個介面,通過介面訪問,
class People: def __init__(self,name,age): self.__name =name self.__age=age def tell_info(self): print('Name:<%s> Age:<%s>' %(self.__name , self.__age)) p=People('egon',18)p.tell_info()
如上述的代碼,我們把隱藏的屬性放在:tell_info()中,外部的執行個體化對象,要想訪問,就通過執行個體對象.tell_info()這種方法,這就要通過介面方法來訪問的,方便快捷;
2.同樣上述的代碼,使用者不能對name,age直接進行修改了,我們需要開一個介面可以供使用者進行修改;
class People: def __init__(self,name,age): self.__name =name self.__age=age def tell_info(self): print('Name:<%s> Age:<%s>' %(self.__name , self.__age)) def set_info(self,name,age): if not isinstance(name,str): print("名字是字串") return if not isinstance(age,str): print("年齡是字串") return self.__name =name self.__age=age p=People('egon',18)p.tell_info()P.set_info('EGON',38)#這裡我們用的開的介面來修改的P.set_info('egon','12')
- 這就是我們資料屬性的封裝,明確的區分內外,控制外部對影藏屬性的操作行為;
封裝方法的目的
例如我有一個取款的例子:
class ATM: def __card(self): print('插卡') def __auth(self): print('使用者認證') def __input(self): print('輸入取款金額') def __print——bill(self): print('列印賬單') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()
>* 如上述的代碼可以看出,我們外部的執行個體通過,withdraw可以訪問完全;
封裝與擴充性:
以前我們說過:物件導向的優點有:可擴充性高,其實封裝也是可擴充性的高的一個方面;
class Room: def __init__(self,namek,owner,height,weight,length) self.name = name self.owner=owner #如果只想讓使用者訪問:房間的長度和寬度,這裡我們就可以把寬度和長度變為隱藏; self.__weight=weight self.__length=lengthr=Room('房間','wo',10,10)
1.如果問題來了,我們要只想訪問房間的面積而不是房間的屬性呢?
class Room: def __init__(self,namek,owner,height,weight,length) self.name = name self.owner=owner #如果只想讓使用者訪問:房間的長度和寬度,這裡我們就可以把寬度和長度變為隱藏; self.__weight=weight self.__length=length def tell_area(self): return self._weight*self.__length r=Room('房間','wo',10,10)print(r.tell_area())
如上述我們使用者,只需要關心的是:r.tell_area()究竟裡面的邏輯如何我們不用管,這就為我們提供了方便,類的擴充性;
class Room: def __init__(self,namek,owner,height,weight,length,height) self.name = name self.owner=owner #如果只想讓使用者訪問:房間的長度和寬度,這裡我們就可以把寬度和長度變為隱藏; self.__weight=weight self.__length=length self.__height=height def tell_area(self): return self._weight*self.__length*self.__height r=Room('房間','wo',10,10)print(r.tell_area())
同樣對於使用者來說,他們不需關心內部的處理邏輯,他們只需要調用就好了;
property的使用:
例如:如下描述:
實現BMI指數程式:
class People: def __init__(self,name,weiht,height): self.name=name self.weight=weigth self.height=heightP=People('ll',1.81,75)bmi=p.weight/(p.height ** 2)#或者可以寫成如下的:p.bmi =p.weight / (p.height **2)print(p.bmi)
問題:
如上述的方法bmi,雖然我們實現了,但是我們每次都要定義bmi,是不是很麻煩,而且bmi不是固定的,是隨著人的身高體重變化的,而不是一成不變的,那麼問題來了,我們怎麼實現簡單化呢?
- 這裡我們可以把bmi,定義成為方法,然後放置在類裡面 ,這樣以後的執行個體只需要調用這個方法就好了,不用每次執行個體化去計算了;
class People: def __init__(self,name,weiht,height): self.name=name self.weight=weigth self.height=height def bmi(self): return self.weight/(p.height ** 2) P=People('ll',1.81,75)print(p.bmi())#bmi後邊的()意味著調用函數
如上述的代碼,對於使用者而言,他把bmi看成指數,加()他們反而不懂,或者說是會誤解,對於使用者他們就把指數bmi當成了一個名詞,而不是()的方法;使用者最終想實現的其實就是p.bmi
所以要實現p.bmi我要在class類裡面,bmi()上邊加個,裝飾器:
class People: def __init__(self,name,weiht,height): self.name=name self.weight=weigth self.height=height @property def bmi(self): return self.weight/(p.height ** 2)P=People('ll',1.81,75)print(p.bmi())#bmi後邊的()意味著調用函數
補充一個知識點:property的使用:
比如自己定義一個property的用法;
class People: def __init__(sefl,name): self.__name=namep =People('egon')
如上述我們肯定擷取不到名字怎麼辦呢?
答案:我們肯定要通過介面來擷取名字
class People: def __init__(sefl,name): self.__name=name def get_name(self): return self.__name p =People('egon') print(p.get_name())
然後對於使用者而言:他們就想訪問屬性而已,不用通過方法,要不然使用者會懵逼;
那麼問題來了,我們怎麼來搞呢?(答案只有加裝飾器了,如代碼)
class People: def __init__(sefl,name): self.__name=name @property def get_name(self): return self.__name p =People('egon') print(p.name)
這裡我們通過property來說明,另一個property的用法:
class People: def __init__(sefl,name): self.__name=name @property def name(sefl): return self.__name @name.setter def name(self,val): if not isinstance(val,str): print('名字必須是字串類型的') return self.__name =val @name.deleter def name(self): print('不讓刪除') p =People('egon') p.name #這裡觸發的是第一個,return self.__namep.name='DDD'#這個是訪問行為,觸發的是def name(self,val),如上述的修改的邏輯del p.name#刪除屬性
如上述所述:我們之要掌握,不需要掌握太多;
@property def name(sefl): return self.__name
- 總結,如果說一些東西不能像屬性一樣,需要定一個方法來實現,但是使用者又想把這些當做屬性來訪問,而不是方法,()之類的東西,所以要這裡我們需要使用property裝飾之類東西來滿足需要;
這裡上述的
@property def name(sefl): return self.__name
- 就是一些東西無法通過屬性直接存取的,所以就定義一個方法,然後呢,我們使用者又不懂什麼方法不方法的,使用者要按照屬性一個的訪問,所以這時候就用到了裝飾器;
物件導向之繼承