本文目錄一覽:
- 1、python繼承父類 怎麼引用
- 2、Python的類和對象入門
- 3、Python程序中調用子類時直接修改父類的屬性
- 4、Python繼承父類parent的正確格式為
- 5、Python類的多重繼承問題深入分析
- 6、python 多重繼承,繼承的幾個父類都需要傳遞參數,怎麼在子類計算出父類傳遞的參數總和呢?
python繼承父類 怎麼引用
子類調用父類函數有以下方法:
直接寫類名調用
用 super(type, obj).method(arg)方法調用。
在類定義中調用本類的父類方法,可以直接用super().method(arg)
1
2
3
4
5
6
7
8
9
class A:
def method(self, arg):
pass
class B(A):
def method(self, arg):
# A.method(self,arg) # 1
# super(B, self).method(arg) # 2
super().method(arg) # 3
Python的類和對象入門
本文來說說Python中的類與對象,Python這門語言是無處不對象,如果你曾淺要了解過Python,你應該聽過Python是一種面向對象編程的語言,所以你經常可能會看到面向「對象」編程這類段子,而面向對象編程的語言都會有三大特徵:封裝、繼承、多態。
我們平時接觸到的很多函數、方法的操作都具有這些性質,我們只是會用,但還沒有去深入了解它的本質,下面就介紹一下關於類和對象的相關知識。
封裝這個概念應該並不陌生,比如我們把一些數據封裝成一個列表,這就屬於數據封裝,我們也可以將一些代碼語句封裝成一個函數方便調用,這就是代碼的封裝,我們也可以將數據和代碼封裝在一起。用術語表示的話,就是可以將屬性和方法進行封裝,從而得到對象。
首先我們可以定義一個類,這個類中有屬性和方法,但有的夥伴會比較好奇,屬性和方法不是會封裝成對象嘛,為什麼又變成類了?舉個例子,類就好比是一個毛坯房,而對象是在毛坯房的基礎上改造成的精裝房。
在類定義完成時就創建了一個類對象,它是對類定義創建的命名空間進行了一個包裝。類對象支持兩種操作:屬性引用和實例化。
屬性引用的語法就是一般的標準語法:obj.name。比如XiaoMing.height和XiaoMing.run就是屬性引用,前者會返回一條數據,而後者會返回一個方法對象。
這裡也支持對類屬性進行賦值操作,比如為類中的weight屬性賦予一個新值。
而類的實例化可以將類對象看作成一個無參函數的賦值給一個局部變數,如下:
ming就是由類對象實例化後創建的一個實例對象,通過實例對象也可以調用類中的屬性和方法。
類在實例化過程中並不都是像上面例子一樣簡單的,一般類都會傾向將實例對象創建為有初始狀態的,所以在類中可能會定義一個__init__的魔法方法,這個方法就可以幫助接收、傳入參數。
而一個類如果定義了__init__方法,那麼在類對象實例化的過程中就會自動為新創建的實例化對象調用__init__方法,請看下面這個例子。
可以看到在__init__()中傳入了參數x和y,然後在print_coor中需要接收參數x和y,接下來通過實例化這個類對象,驗證一下參數是否能通過__init__()傳遞到類的實例化操作中。
所謂繼承就是一個新類在另一個類的基礎上構建而成,這個新類被稱作子類或者派生類,而另一個類被稱作父類、基類或者超類,而子類會繼承父類中已有的一些屬性和方法。
比如上面這個例子,我並沒有將list_定義成一個列表,但它卻能調用append方法。原因是類Mylist繼承於list這個基類,而list_又是Mylist的一個實例化對象,所以list_也會擁有父類list擁有的方法。當然可以通過自定義類的形式實現兩個類之間的繼承關係,我們定義Parent和Child兩個類,Child中沒有任何屬性和方法,只是繼承於父類Parent。
當子類中定義了與父類中同名的方法或者屬性,則會自動覆蓋父類對應的方法或屬性,還是用上面這個例子實現一下,方便理解。
可以看到子類Child中多了一個和父類Parent同名的方法,再實例化子類並調用這個方法時,最後調用的是子類中的方法。Python中繼承也允許多重繼承,也就是說一個子類可以繼承多個父類中的屬性和方法,但是這類操作會導致代碼混亂,所以大多數情況下不推薦使用,這裡就不過多介紹了。
多態比較簡單,比如定義兩個類,這兩個類沒有任何關係,只是兩個類中有同名的方法,而當兩個類的實例對象分別調用這個方法時,不同類的實例對象調用的方法也是不同的。
上面這兩個類中都有introduce方法,我們可以實例化一下兩個類,利用實例對象調用這個方法實現一下多態。
判斷一個類是否是另一個類的子類,如果是則返回True,反之則返回False。
需要注意的有兩點:
判斷一個對象是否為一個類的實例對象,如果是則返回True,反之則返回False。
需要注意的有兩點:
判斷一個實例對象中是否包含一個屬性,如果是則返回True,反之則返回False。
需要注意的是第二個參數name必須為字元串形式傳入,如果不是則會返回False。
Python程序中調用子類時直接修改父類的屬性
在學習類的繼承時,被「在子類中調用和修改父類的屬性」卡住了。通過在網上查閱資料,大致搞明白了。主要有以下三點:
1. 子類的 __init__ 方法中通過「 父類名.__init__(self) 」語句將父類的屬性繼承過來,見下面示例中標註「#例2」處。
2. 如果要在程序中調用子類時直接通過參數的形式修改父類繼承過來的屬性,那麼子類的 __init__ 方法中需要在self後面跟上要賦值的參數,見下面示例中標註「#例1」處。
3. 即使是通過「 父類名.__init__(self) 」語句繼續過來的父類的屬性,也需要在子類的 __init__ 方法中使用 self.變數名=變數值 的方式進行賦值,否則在子類中無法引用變數,見下面示例中標註「#例3」和「#例4」處。
執行結果:
從以上執行結果看,我們通過 y=b(4,5) 方式直接調用子類並給出參數「4,5」,而使用 y.myprint 調用了父類的方法,最後的計算結果是根據子類的參數計算得出的。說明我們在調用子類時直接修改了父類aa和bb的屬性。
Python繼承父類parent的正確格式為
格式:
class 子類名(父類1,父類2)
類的繼承就是讓子類擁有父類的屬性和方法。
幾個注意:py支持多繼承
子類繼承的父類只能初始化一次,如果父類1和父類2有共同的父類或者祖先類,則類初始化的時候會失敗。
當父類具有相同方法時,會調用最先繼承的父類中的方法,如果要指定父類,則需要重寫此方法,並通過父類名.方法名來調用指定父類方法。
Python類的多重繼承問題深入分析
Python類的多重繼承問題深入分析
首先得說明的是,Python的類分為經典類 和 新式類
經典類是python2.2之前的東西,但是在2.7還在兼容,但是在3之後的版本就只承認新式類了
新式類在python2.2之後的版本中都可以使用
經典類和新式類的區別在於:
經典類是默認沒有派生自某個基類的,而新式類是默認派生自object這個基類的:
代碼如下:
# old style
class A():pass
# new style
class A(obejct):pass
2.經典類在類多重繼承的時候是採用從左到右深度優先原則匹配方法的..而新式類是採用C3演算法(不同於廣度優先)進行匹配的
3.經典類是沒有__MRO__和instance.mro()調用的,而新式類是有的.
為什麼不用經典類,要更換到新式類
因為在經典類中的多重繼承會有些問題…可能導致在繼承樹中的方法查詢繞過後面的父類:
代碼如下:
class A():
def foo1(self):
print “A”
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print “C”
class D(B, C):
pass
d = D()
d.foo1()
按照經典類的查找順序從左到右深度優先的規則,在訪問d.foo1()的時候,D這個類是沒有的..那麼往上查找,先找到B,裡面沒有,深度優先,訪問A,找到了foo1(),所以這時候調用的是A的foo1(),從而導致C重寫的foo1()被繞過.
所以python引入了新式類的概念,每個基類都繼承自object並且,他的匹配規則也從深度優先換到了C3
C3演算法
C3演算法是怎麼做匹配的呢..在問答版塊上面討論之後,歸結如下:
C3演算法的一個核心是merge.
在merge列表中,如果第一個序列mro的第一個類是出現在其它序列,並且也是第一個,或者不出現其它序列,那麼這個類就會從這些序列中刪除,併合到訪問順序列表中
比如:(引用問題中zhuangzebo的回答@zhuangzebo)
代碼如下:
class A(O):pass
class B(O):pass
class C(O):pass
class D(A,B):pass
class E(C,D):pass
首先需要知道 O(object)的mro(method resolution order)列表是[O,]
那麼接下來是:
代碼如下:
mro(A) = [A, O]
mro(B) = [B, O]
mro(C) = [C, O]
mro(D) = [D] + merge(mro(A), mro(B), [A, B])
= [D] + merge([A, O], [B, O], [A, B])
= [D, A] + merge([O], [B, O], [B])
= [D, A, B] + merge([O], [O])
= [D, A, B, O]
mro(E) = [E] + merge(mro(C), mro(D), [C, D])
= [E] + merge([C, O], [D, A, B, O], [C, D])
= [E, C] + merge([O], [D, A, B, O], [D])
= [E, C, D] + merge([O], [A, B, O])
= [E, C, D, A, B] + merge([O], [O])
= [E, C, D, A, B, O]
然後還有一種特殊情況:
比如:
merge(DO,CO,C) 先merge的是D
merge(DO,CO,C) 先merge的是C
意思就是.當出現有 一個類出現在兩個序列的頭(比如C) 這種情況和 這個類只有在一個序列的頭(比如D) 這種情況同時出現的時候,按照順序方式匹配。
新式類生成的訪問序列被存儲在一個叫MRO的只讀列表中..
你可以使用instance.__MRO__或者instance.mro()來訪問
最後匹配的時候就按照MRO序列的順序去匹配了
C3和廣度優先的區別:
舉個例子就完全明白了:
代碼如下:
class A(object):pass
class B(A):pass
class C(B):pass
class D(A):pass
class E(D):pass
class F(C, E):pass
按照廣度優先遍歷,F的MRO序列應該是[F,C,E,B,D,A]
但是C3是[F,E,D,C,B,A]
意思是你可以當做C3是在一條鏈路上深度遍歷到和另外一條鏈路的交叉點,然後去深度遍歷另外一條鏈路,最後遍歷交叉點
新式類和經典類的super和按類名訪問問題
在經典類中,你如果要訪問父類的話,是用類名來訪問的..
代碼如下:
class A():
def __init__(self):
print “A”
class B(A):
def __init__(self):
print “B”
A.__init__(self) #python不會默認調用父類的初始化函數的
這樣子看起來沒三問題,但是如果類的繼承結構比較複雜,會導致代碼的可維護性很差..
所以新式類推出了super這個東西…
代碼如下:
class A():
def __init__(self):
print “A”
class B(A):
def __init__(self):
print “B”
super(B,self).__init__()
這時候,又有一個問題:當類是多重繼承的時候,super訪問的是哪一個類呢?
super實際上是通過__MRO__序列來確定訪問哪一個類的…實際上就是調用__MRO__中此類後面的一個類的方法.
比如序列為[F,E,D,C,B,A]那麼F中的super就是E,E的就是D
super和按照類名訪問 混合使用帶來的坑
代碼如下:
class A(object):
def __init__(self):
print “enter A”
print “leave A”
class B(object):
def __init__(self):
print “enter B”
print “leave B”
class C(A):
def __init__(self):
print “enter C”
super(C, self).__init__()
print “leave C”
class D(A):
def __init__(self):
print “enter D”
super(D, self).__init__()
print “leave D”
class E(B, C):
def __init__(self):
print “enter E”
B.__init__(self)
C.__init__(self)
print “leave E”
class F(E, D):
def __init__(self):
print “enter F”
E.__init__(self)
D.__init__(self)
print “leave F”
這時候列印出來是:
代碼如下:
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
可以看出來D和A的初始化函數被亂入了兩次!
按類名訪問就相當於C語言之前的GOTO語句…亂跳,然後再用super按順序訪問..就有問題了
所以建議就是要麼一直用super,要麼一直用按照類名訪問
最佳實現:
避免多重繼承
super使用一致
不要混用經典類和新式類
調用父類的時候注意檢查類層次
以上便是本人對於python類的繼承的認識了,希望對大家能有所幫助
python 多重繼承,繼承的幾個父類都需要傳遞參數,怎麼在子類計算出父類傳遞的參數總和呢?
運行你的代碼:出錯位置: c = C()
出錯結果:TypeError: __init__() missing 1 required positional argument: ‘ num1 ‘
先來看你的程序__main()__部分:a = A(2) 和 b = B(5) 這是類A和類B的一個實例。在python中實例變數是用於每個實例的唯一數據,這就說明你這裡的傳遞參數2或者是5隻能用在實例化的 a 或者是 b 下才有作用。 那麼重點看 c = C( ) ,c是類對象C的實例化,c 只能用自身實例變數才有用,因此前面的實例 a 下的變數 num1=2 和 實例 b 下的變數 num1=5對實例c是無用的。所以,出錯結果很明顯了缺少傳遞的位置參數了。這為什麼提示缺少1個位置參數呢?下面為你簡單講解一下吧。你也可以用內置方法__mro__() :查看方法或者屬性的調用路徑——print(類名.__mro__)
類C是多繼承類A和類B的,多繼承(不存在super()重寫方法下),類C的實例化c是怎麼工作的——對於實例c調用方法或屬性過程是這樣的:查找當前類C中是否存在,然後在多個父類中按照從左往右順序查找(類A中先查找,類B中後查找),只要在這個過程中找到就退出了,後面的就不再查找了。
好吧,給你分析一下你程序的過程:類A和類B中都有__init__()同一個方法,方法相同那首先就查找唄——先查找類C(沒有對__init__()進行修改,那就是跳過了),然後再去類A查找,好嘛這就找到了__init__(self, num1),找到了就退出了。所以這樣一看對類C進行實例化就需要傳遞一個參數給num1就夠了。你也可以交換繼承位置class C(B, A),這樣就是類C實例化需要傳遞一個參數給num2就夠了。這樣過程就清晰了。
好第三個問題來了:你類C中有兩個參數呀num1和num2,而實例化又僅需要一個參數就夠了,這樣就肯定會產生問題了。
不信你試試給c = C(2)產生錯誤:AttributeError: ‘C’ object has no attribute ‘num2’
解決方法1:既然沒有屬性num2就在類C中刪掉就是了,然後c = C(2)就可以運行成功了。
解決方案2:類變數用於類的所有實例共享的屬性和方法。因此,要想共用這兩個變數num1和num2,就得讓搜索的時候不要進到類A和類B中前提下,將它們變成對應的類變數就可以了。第一個前提很好實現:在類C下 定義:def __init__(self) : pass 第二個條件也比較好實現:將類A或類B的 __init__(self, num) : X.num = num X為對應的類名。(說明:self表示類實例化對象,即self.num 表示實例變數;X表示類對象,則X.num表示類變數),這樣就可以共享類A和類B的變數了。
class A:
def __init__(self, num1):
A.num1 = num1
class B:
def __init__(self, num2):
B.num2 = num2
class C(A, B):
def __init__(self):
pass
def num_sum(self):
return self.num2 + self.num1
if __name__ == ‘__main__’:
a = A(2)
b = B(5)
c = C()
print(c.num_sum())
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/272153.html