日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

继承、派生、组合

發布時間:2025/3/17 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 继承、派生、组合 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

繼承

類之間會有一些相同的屬性,提取這些相同的屬性做成基類(父類)
繼承是創建類的一種方式,通過代碼重用,減少代碼冗余。把父類的屬性遺傳給子類。
在創建類時,新建的類可以繼承一個或多個父類,方式如下:

class ClassName(BaseName1, BaseName2,...): # 括號內是繼承的父類'類注釋文檔'pass

繼承是一種類與類之間的關系: 什么 是 什么
查看所有繼承:ClassName.__bases__ ,返回元組。
在python3中,所有類默認繼承object。
在python2中,繼承了object的子類都稱為新式類,而沒有繼承object及其子類的,稱為經典類。
繼承的子類會獲得父類的所有屬性。對象在調用屬性時,尋找順序如下:
對象名稱空間 >>> 所屬類名稱空間 >>> 父類名稱空間
單純的繼承沒有意義,子類需要派生自己新的屬性:

class People(): # 默認繼承objectdef __init__(self, name, age, sex):print('initializing...')self.name = nameself.age = ageself.sex= sexdef walk(self):print('%s can walk well' % self.name)class Teacher(People):def __init__(self, name, age, sex, salary, level):People.__init__(self, name, age, sex) # 繼承父類的屬性# 派生子類自己的數據屬性self.salary = salaryself.level = level# 派生子類自己的函數屬性,父類中有的就不用重復寫了。def teach(self):print('%s is good at teaching' % self.name)

組合

組合也是一種類與類之間的關系: 什么 有 什么 。也是通過代碼重用,減少代碼冗余。

class Date():def __init__(self,year,mon,day):self.year = yearself.mon = monself.day = daydef tell_info(self):print('birth is %s-%s-%s' % (self.year, self.mon, self.day))class Teacher():def __init__(self, name, age, *args): # 這里用*args接收year,mon,dayself.name = nameself.age = ageself.birth = Date(*args) # birth屬性的是Date類的對象。t = Teacher('egon',18,1990,2,30) # 1990,2,30 這三個參數傳給Date中的__init__函數 t.birth.tell_info() ''' birth is 1990-2-30 ''' class People:def __init__(self,name,age,sex): # 函數的默認參數不要寫成可變的,所以couser=[]不要寫在形參位置self.name = nameself.age = ageself.sex = sexself.course = []def tell_info(self):print('''----- %s info -----NAME: %sAGE: %sSEX: %s'''%(self.name,self.name, self.age,self.sex))def tell_course(self):if self.course:for i in self.course:print(i.tell_info())else:print('no course added!')# def tell_couser(self):# if 'couser' in self.__dict__: # 如果不設couser默認參數,那么就沒有couser屬性,# 如果用 if self.couser: 就會報錯。判斷字符串在名稱空間的字典就沒問題# for i in self.couser:# print(i.tell_info)# else:# print('no couser added')class Teacher(People):def __init__(self,name,age,sex,salary,level): # 函數的默認參數不要寫成可變的,所以couser=[]不要寫在形參位置People.__init__(self,name,age,sex)self.salary = salaryself.level = levelclass Student(People):def __init__(self,name,age,sex,group):People.__init__(self,name,age,sex)self.group=groupclass Date:def __init__(self,year,mon,day):self.year = yearself.mon = monself.day = daydef tell_info(self,obj):print('%s 出生于:%s-%s-%s' % (obj.name,self.year, self.mon, self.day))class Course:def __init__(self,name,price,period):self.name = nameself.price = priceself.period = perioddef tell_info(self):print('''----- %s info -----name: %sprice: %speriod: %s'''%(self.name,self.name,self.price,self.period))Birth = Date(1990,2,31) # 創建時間對象 python = Course('python',15800,'6monts') # 創建課程對象python go = Course('go', 10000, '6monts') # 創建課程對象 goalex = Teacher('alex',84,'female',300,1) # 創建老師對象 alex.birth = Birth # 將日期組合進老師對象 alex.course.append(python) # 將課程對象組合進老師對象 alex.course.append(go) alex.birth.tell_info(alex) alex.tell_course() alex.tell_info()ayhan = Student('ayhan',18,'male','group7') ayhan.tell_course()

接口與歸一化設計

接口,隱藏具體的實現細節,將使用簡單化。比如,在linux中,一切皆文件,比如文本文件、磁盤文件、進程文件,都有讀寫操作,使用者不需要關心每種文件的讀和寫是如何具體實現的,只需要知道只要是文件,就應該有讀方法和寫方法,調用這兩個方法,就可以完成想要的效果。我們可以通過繼承,來模擬這種情況:

class File(): # 定義文件類來模仿接口的概念def read(self): # 定義接口函數readpassdef write(self): # 定義接口函數writepassclass Txt(File): # 具體實現文本文件的read和writedef read(self):print('文本文件讀方法') # 這里用print來模擬,具體的實現可能是很復雜的。def write(self):print('文本文件讀方法')class Sata(File): # 具體實現磁盤文件的read和writedef read(self):print('磁盤文件讀方法')def write(self):print('磁盤文件寫方法')class Process(File): # 具體實現進程文件的read和writedef read(self):print('進程文件讀方法')def write(self):print('進程文件寫方法')t = Txt() s = Sata() p = Process() # 具體到三個不同的對象,使用者只需要知道它們是文件,是文件就有read和write方法: # t.read() t.read() s.read() s.write() p.read() p.write()

上面這個栗子中,定義的File這個父類,只放方法名(read, write),相當于一個模板,具體的功能由子類來實現。
但是,子類在定義時,并不一定要遵循父類中的方法,因此,父類有必要對此加以限制:
1. 子類必須要有父類的方法
2. 子類實現的方法必須跟父類的方法的名字一樣

# 知識準備:抽象類就是基于類抽象而來的。 # 抽象類中只能有抽象方法(沒有實現功能),該類不能被實例化,只能被繼承,且子類必須實現抽象方法 import abc # 導入abc模塊實現抽象類class File(metaclass=abc.ABCMeta):@abc.abstractmethoddef read(self):pass@abc.abstractmethoddef write(self):pass

通過上面這種方式,我們再定義子類時,就必須要有父類的方法,并且方法名一樣,否則就無法實例化對象:

class Txt(File): # 父類是Filedef du(self):print('文本文件讀方法')def xie(self):print('文本文件寫方法')t = Txt() '''雖然上面的Txt類在定義階段沒問題,但是在實例化創建對象時,會提示無法實例化抽象類,t = Txt() TypeError: Can't instantiate abstract class Txt with abstract methods read, write '''

這種把所有方法都統一起來的方式,就是歸一化設計,方便使用。

對象的序列化

pickle可以序列化任何python的數據類型

import pickleclass Sample: # 定義類passobj = Sample() # 創建對象with open(file,'wb')as f: # 序列化pickle.dump(obj,f)with open(file, 'rb')as f: # 反序列化obj = pickle.load(f)

注意,對象是依賴于類的,如果反序列化出的對象沒有在內存中找到所屬的類,就會報錯。解決這個問題,可以通過導入模塊的方式,將類導入。因為導入會執行模塊的內容,加載到內存。

繼承的實現原理MRO

情況一:


屬性的查找,從左到右,一條條分支的找。這種情況下,經典和新式類尋找都一樣:
DAE > B > C

情況二


新式類:HEB>FC>GDA 最后一個分支時才找到頭。廣度優先。
經典類:一條分支找到頭,再找下一個分支。深度優先。
HEBA > FC > GD

class A:passdef foo(self):print('from A')class B(A):pass# def foo(self):# print('from B')class C(A):passdef foo(self):print('from C')class D(A):passdef foo(self):print('from D')class E(B):pass# def foo(self):# print('from E')class F(C):passdef foo(self):print('from F')class G(D):passdef foo(self):print('from G')class H(E,F,G):pass# def foo(self):# print('from H')obj = H() obj.foo() # 經典類打印結果是 from A # 經典類查找:HEBA > FC > GD 深度優先,一個分支找到頭,再找下一個分支 # 如果讓class A(object) 繼承object,成為新式類,那么,打印結果是 from F # 新式類查找:HEB > FC > GDA > (object) 廣度優先,在F時找到

再說self

self就是調用方法的對象本身!無論何時,找方法時先回自己類開始找!

class A(object):def f3(self):print('A.f3')class B(object):def f1(self):print('B.f1')self.f2()class C(B):def f2(self):print('C.f2')self.f3()def f3(self):print('C.f3')class D(C):def f3(self):print('D.f3')obj = D() obj.f1()''' 打印結果 B.f1 C.f2 D.f3 '''

mro()繼承屬性的查找順序

print(H.mro()) 查看繼承,只在新式類有這個屬性:
[<class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.C'>, <class '__main__.G'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]

子類調用父類的方法

class People:def __init__(self,name,age,sex):self.name=nameself.age=ageself.sex=sexclass Teacher(People):def __init__(self,name,age,sex,level):# 指明道姓的調用父類的屬性People.__init__(self,name,age,sex,) # 明確寫出父類的名稱,如果父類改了,那么這里也要改。self.level=levelclass Student(People):def __init__(self,name,age,sex,group):# 通過super()調用父類的屬性super().__init__(name,age,sex) # super()自動傳入'Student' 'self'兩個參數,產生一個對象# 對象調用函數是綁定方法,因此__init__()的第一個參數self也不用手動傳入了。self.group=group

在python2中使用super() 子類名和self這個兩個參數還是要手動傳,即super(子類名,self)
另外,只有新式類才可以使用super() 函數,因為該函數在尋找繼承屬性時,是根據mro列表。并且是只要找到一個父類的屬性,就會停止。因此,如果子類中要調用多個父類中的同名屬性時,還是要用指名道姓的方式。

總結

以上是生活随笔為你收集整理的继承、派生、组合的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。