Python面向?qū)ο缶幊讨^承與多態(tài)詳解
目前創(chuàng)新互聯(lián)建站已為數(shù)千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間、網(wǎng)站托管運(yùn)營、企業(yè)網(wǎng)站設(shè)計、新巴爾虎左網(wǎng)站維護(hù)等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
本文實例講述了Python面向?qū)ο缶幊讨^承與多態(tài)。分享給大家供大家參考,具體如下:
Python 類的繼承
在OOP(Object Oriented Programming)程序設(shè)計中,當(dāng)我們定義一個class的時候,可以從某個現(xiàn)有的class 繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。
我們先來定義一個class Person,表示人,定義屬性變量 name 及 sex (姓名和性別);
定義一個方法print_title():當(dāng)sex是male時,print man;當(dāng)sex 是female時,print woman。參考如下代碼:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person): # Child 繼承 Person
pass
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex) # 子類繼承父類方法及屬性
May.print_title()
Peter.print_title()
而我們編寫 Child 類,完全可以繼承 Person 類(Child 就是 Person);使用 class subclass_name(baseclass_name) 來表示繼承;
繼承有什么好處?最大的好處是子類獲得了父類的全部屬性及功能。如下 Child 類就可以直接使用父類的 print_title() 方法
實例化Child的時候,子類繼承了父類的構(gòu)造函數(shù),就需要提供父類Person要求的兩個屬性變量 name 及 sex:
在繼承關(guān)系中,如果一個實例的數(shù)據(jù)類型是某個子類,那它也可以被看做是父類(May 既是 Child 又是 Person)。但是,反過來就不行(Peter 僅是 Person,而不是Child)。
繼承還可以一級一級地繼承下來,就好比從爺爺?shù)桨职?、再到兒子這樣的關(guān)系。而任何類,最終都可以追溯到根類object,這些繼承關(guān)系看上去就像一顆倒著的樹。比如如下的繼承樹:
isinstance() 及 issubclass()
Python 與其他語言不同點(diǎn)在于,當(dāng)我們定義一個 class 的時候,我們實際上就定義了一種數(shù)據(jù)類型。我們定義的數(shù)據(jù)類型和Python自帶的數(shù)據(jù)類型,比如str、list、dict沒什么兩樣。
Python 有兩個判斷繼承的函數(shù):isinstance() 用于檢查實例類型;issubclass() 用于檢查類繼承。參見下方示例:
class Person(object):
pass
class Child(Person): # Child 繼承 Person
pass
May = Child()
Peter = Person()
print(isinstance(May,Child)) # True
print(isinstance(May,Person)) # True
print(isinstance(Peter,Child)) # False
print(isinstance(Peter,Person)) # True
print(issubclass(Child,Person)) # True
Python 類的多態(tài)
在說明多態(tài)是什么之前,我們在 Child 類中重寫 print_title() 方法:若為male,print boy;若為female,print girl
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person): # Child 繼承 Person
def print_title(self):
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title()
Peter.print_title()
當(dāng)子類和父類都存在相同的 print_title()方法時,子類的 print_title() 覆蓋了父類的 print_title(),在代碼運(yùn)行時,會調(diào)用子類的 print_title()
這樣,我們就獲得了繼承的另一個好處:多態(tài)。
多態(tài)的好處就是,當(dāng)我們需要傳入更多的子類,例如新增 Teenagers、Grownups 等時,我們只需要繼承 Person 類型就可以了,而print_title()方法既可以直不重寫(即使用Person的),也可以重寫一個特有的。這就是多態(tài)的意思。調(diào)用方只管調(diào)用,不管細(xì)節(jié),而當(dāng)我們新增一種Person的子類時,只要確保新方法編寫正確,而不用管原來的代碼。這就是著名的“開閉”原則:
對擴(kuò)展開放(Open for extension):允許子類重寫方法函數(shù)
對修改封閉(Closed for modification):不重寫,直接繼承父類方法函數(shù)
子類重寫構(gòu)造函數(shù)
子類可以沒有構(gòu)造函數(shù),表示同父類構(gòu)造一致;子類也可重寫構(gòu)造函數(shù);現(xiàn)在,我們需要在子類 Child 中新增兩個屬性變量:mother 和 father,我們可以構(gòu)造如下(建議子類調(diào)用父類的構(gòu)造方法,參見后續(xù)代碼):
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person): # Child 繼承 Person
def __init__(self,name,sex,mother,father):
self.name = name
self.sex = sex
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
若父類構(gòu)造函數(shù)包含很多屬性,子類僅需新增1、2個,會有不少冗余的代碼,這邊,子類可對父類的構(gòu)造方法進(jìn)行調(diào)用,參考如下:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person): # Child 繼承 Person
def __init__(self,name,sex,mother,father):
Person.__init__(self,name,sex) # 子類對父類的構(gòu)造方法的調(diào)用
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
多重繼承
多重繼承的概念應(yīng)該比較好理解,比如現(xiàn)在需要新建一個類 baby 繼承 Child , 可繼承父類及父類上層類的屬性及方法,優(yōu)先使用層類近的方法,代碼參考如下:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person):
pass
class Baby(Child):
pass
May = Baby("May","female") # 繼承上上層父類的屬性
print(May.name,May.sex)
May.print_title() # 可使用上上層父類的方法
class Child(Person):
def print_title(self):
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
class Baby(Child):
pass
May = Baby("May","female")
May.print_title() # 優(yōu)先使用上層類的方法
Python中有兩個特殊的方法, 一個是構(gòu)造函數(shù) init , 另一個是析構(gòu)函數(shù) del ,統(tǒng)稱為魔術(shù)方法。
構(gòu)造函數(shù) init ,創(chuàng)建實例對象之后Python會自動執(zhí)行此方法,把初始化的屬性特點(diǎn)放到實例對象里。
構(gòu)造函數(shù)是創(chuàng)建并初始對象屬性,那么對象使用完成后,系統(tǒng)是怎么處理這些呢?
這個時候,Python引入了銷毀對象功能的析構(gòu)函數(shù) del ()
析構(gòu)函數(shù) del 是對象沒有被引用時會觸發(fā)垃圾回收機(jī)制,進(jìn)行內(nèi)存釋放.
python 內(nèi)置的 del 方法稱為析構(gòu)方法。用于實現(xiàn)對象被銷毀時所需的操作。
常見的應(yīng)用常見如:
析構(gòu)方法 del ()是可選的,如果不提供,則Python 會在后臺提供默認(rèn)析構(gòu)函數(shù)
如果要顯式的調(diào)用析構(gòu)函數(shù),可以使用del關(guān)鍵字: del obj
析構(gòu)方法的作用是銷毀對象的,在python中采用垃圾回收機(jī)制。
Python垃圾回收機(jī)制核心思想是:
詳細(xì)說明:
我們主動刪除對象調(diào)用del 對象;程序運(yùn)行結(jié)束后,python也會自動進(jìn)行刪除其他的對象。
注意:
如果我們重寫子類的 del () 方法(父類為非 object 的類),則必須顯式調(diào)用父類的 del () 方法,這樣才能保證在回收子類對象時,其占用的資源(可能包含繼承自父類的部分資源)能被徹底釋放
我們本期學(xué)習(xí)了Python內(nèi)置函數(shù)析構(gòu)函數(shù),用于沒有被引用的對象進(jìn)行回收處理,一般情況下,我們不用刻意去調(diào)用,python內(nèi)部會對進(jìn)行觸發(fā)。
以上是本期內(nèi)容,歡迎大佬們評論區(qū)指正,下期見~
不行,一個class只能有一個用于構(gòu)造對象的__init__函數(shù)
但python中的變量是無類型的,因此傳給__init__的參數(shù)可以是任何類型
python中的函數(shù)參數(shù)在定義時可以有默認(rèn)值,可以讓__init__函數(shù)接受多個參數(shù),在后面的一些參數(shù)給出默認(rèn)值的方法讓__init__接受不同個數(shù)的參數(shù),并且執(zhí)行類型檢查執(zhí)行不同的代碼,用上述方法實現(xiàn)類的構(gòu)造函數(shù)的多態(tài)性
先小小糾正一下,java里面一般不叫函數(shù),叫方法,這是Java的一個小習(xí)慣。
你這個問題原因很簡單。
首先,任何類都有構(gòu)造方法,難怕是你不寫,也會默認(rèn)你有一個有無參構(gòu)造方法。,所以你的A里面就會有一個叫A()的構(gòu)造方法。
當(dāng)你new A()時,默認(rèn)你有一個有無參構(gòu)造方法A()的方法里的第一句,會自動加上一個super();的方法,這句就是調(diào)用父類構(gòu)造方法的意思,這是java規(guī)定的規(guī)則。
你可以嘗試一下,在A里寫一個構(gòu)造方法:
A(){
super();? ?//這個一定要放在第一句
System.out..XX;
}
這與你不寫super()這句效果是一樣的,因如果沒寫,java會默認(rèn)在第一句加上super。
1.構(gòu)造函數(shù)的命名必須和類名完全相同。在java中普通函數(shù)可以和構(gòu)造函數(shù)同名,但是必須帶有返回值;
2.構(gòu)造函數(shù)的功能主要用于在類的對象創(chuàng)建時定義初始化的狀態(tài)。它沒有返回值,也不能用void來修飾。這就保證了它不僅什么也不用自動返回,而且根本不能有任何選擇。而其他方法都有返回值,即使是void返回值。盡管方法體本身不會自動返回什么,但仍然可以讓它返回一些東西,而這些東西可能是不安全的;
3.構(gòu)造函數(shù)不能被直接調(diào)用,必須通過new運(yùn)算符在創(chuàng)建對象時才會自動調(diào)用;而一般的方法是在程序執(zhí)行到它的時候被調(diào)用的;
4.當(dāng)定義一個類的時候,通常情況下都會顯示該類的構(gòu)造函數(shù),并在函數(shù)中指定初始化的工作也可省略,不過Java編譯器會提供一個默認(rèn)的構(gòu)造函數(shù).此默認(rèn)構(gòu)造函數(shù)是不帶參數(shù)的。而一般的方法不存在這一特點(diǎn);
5.構(gòu)造函數(shù)有回滾的效果,構(gòu)造函數(shù)拋出異常時,構(gòu)造的是一個不完整對象,會回滾,將此不完整對象的成員釋放(c++)
6.當(dāng)一個類只定義了私有的構(gòu)造函數(shù),將無法通過new關(guān)鍵字來創(chuàng)建其對象,當(dāng)一個類沒有定義任何構(gòu)造函數(shù),C#編譯器會為其自動生成一個默認(rèn)的無參的構(gòu)造函數(shù)。[1]
7.在Python中構(gòu)造函數(shù)必須通過重寫__init__方法實現(xiàn)
例子:
#!/usr/bin/python
# Filename: class_init.py
class Person:
def __init__(self, name):
self.name = name
def sayHi(self):
print Hello, my name is, self.name
p = Person(Swaroop)
p.sayHi()
這個例子中就是在init方法中定義了參數(shù)name,然后調(diào)用的時候直接用類名person帶上傳參swaroop就行了,swaroop參數(shù)就會傳遞給sayhi(),整個流程就對應(yīng)c中的構(gòu)造函數(shù)。
然后說鉤子,其實就是實現(xiàn)一種內(nèi)操作,有子進(jìn)程的意思但又不是,至于裝飾函數(shù)是不是鉤子好像沒官方說法,我認(rèn)為可以算是。裝飾器就是把一個函數(shù)對象返回給另一個函數(shù)來實現(xiàn)既定的功能,其實就是一種內(nèi)操作。
PS:很多東西都是相關(guān)的,比如方法和它的具體實現(xiàn)功能,等你用到它的功能以后就很好理解了,單純的研究理論也沒什么意思。尤其是這種比較抽象的概念。
區(qū)別:
1.如果在創(chuàng)建對象時不寫參數(shù),調(diào)用的就是無參的構(gòu)造方法??墒侨绻銓懙挠杏袇⒌臉?gòu)造方法,而沒有無參的構(gòu)造方法,那么再“創(chuàng)建對象時不寫參數(shù)”就會報錯,程序會認(rèn)為你知道該怎么做。
如果構(gòu)造方法有參數(shù),在創(chuàng)建對象時傳入了參數(shù),那么就會調(diào)用此方法,這一點(diǎn)和重載類似。
2.沒有參數(shù)的構(gòu)造函數(shù)就是默認(rèn)構(gòu)造函數(shù)。
有參數(shù)的構(gòu)造函數(shù)可用傳遞的參數(shù)給類中的屬性賦初始值或執(zhí)行初始化操作例如訂閱事件等。
構(gòu)造函數(shù)是在創(chuàng)建給定類型的對象時執(zhí)行的類方法。構(gòu)造函數(shù)具有與類相同的名稱,它通常初始化新對象的數(shù)據(jù)成員。
任何時候,只要創(chuàng)建類或結(jié)構(gòu),就會調(diào)用它的構(gòu)造函數(shù)。類或結(jié)構(gòu)可能有多個接受不同參數(shù)的構(gòu)造函數(shù)。構(gòu)造函數(shù)使得程序員可設(shè)置默認(rèn)值、限制實例化以及編寫靈活且便于閱讀的代碼。
如果沒有為對象提供構(gòu)造函數(shù),則默認(rèn)情況下 C# 將創(chuàng)建一個構(gòu)造函數(shù),該構(gòu)造函數(shù)實例化對象,并將所有成員變量設(shè)置系統(tǒng)指定的默認(rèn)值。靜態(tài)類和結(jié)構(gòu)也可以有構(gòu)造函數(shù)。
擴(kuò)展資料
主要特點(diǎn)
1.構(gòu)造函數(shù)的命名必須和類名完全相同。在java中普通函數(shù)可以和構(gòu)造函數(shù)同名,但是必須帶有返回值;
2.構(gòu)造函數(shù)的功能主要用于在類的對象創(chuàng)建時定義初始化的狀態(tài)。它沒有返回值,也不能用void來修飾。這就保證了它不僅什么也不用自動返回,而且根本不能有任何選擇。而其他方法都有返回值,即使是void返回值。盡管方法體本身不會自動返回什么,但仍然可以讓它返回一些東西,而這些東西可能是不安全的;
3.構(gòu)造函數(shù)不能被直接調(diào)用,必須通過new運(yùn)算符在創(chuàng)建對象時才會自動調(diào)用;而一般的方法是在程序執(zhí)行到它的時候被調(diào)用的;
4.當(dāng)定義一個類的時候,通常情況下都會顯示該類的構(gòu)造函數(shù),并在函數(shù)中指定初始化的工作也可省略,不過Java編譯器會提供一個默認(rèn)的構(gòu)造函數(shù).此默認(rèn)構(gòu)造函數(shù)是不帶參數(shù)的。而一般的方法不存在這一特點(diǎn);
5.構(gòu)造函數(shù)有回滾的效果,構(gòu)造函數(shù)拋出異常時,構(gòu)造的是一個不完整對象,會回滾,將此不完整對象的成員釋放(c++)
6.當(dāng)一個類只定義了私有的構(gòu)造函數(shù),將無法通過new關(guān)鍵字來創(chuàng)建其對象,當(dāng)一個類沒有定義任何構(gòu)造函數(shù),C#編譯器會為其自動生成一個默認(rèn)的無參的構(gòu)造函數(shù)。
參考資料:百度百科——構(gòu)造函數(shù)