Python 高級 12

property鹅经、魔法屬性和魔法方法、多重繼承和多繼承

1.5 property

學習目標

? 1. 能夠說出什么要使用 set/get 方法操作屬性

? 2. 能夠說出如何對屬性進行數據有效性控制

? 3. 能夠說出使用 set/get 方法的好處

? 4. 能夠說出說出 property 的作用

? 5. 能夠說出 property 類和 @property 有什么區(qū)別

? 6. 能夠說出語法糖的作用

--------------------------------------------------------------------------------

1.5.1 property 概述

property 本身的意義就是屬性坯汤、性質,在 python 中主要為屬性提供便利的操作方式搀愧。

1.5.2 思考

如果現(xiàn)在需要設計一個 銀行帳戶類 惰聂,這個類中包含了帳戶人姓名和帳戶余額,不需要考慮具體的操作接口咱筛,你會怎么設計搓幌?

1.5.3 實現(xiàn)與復盤

簡單實現(xiàn)

class Account(object):

? ? def __init__(self, name, money):

? ? ? ? self.name = name? ? # 帳戶人姓名

? ? ? ? self.balance = money? ? # 帳戶余額

這樣的設計有什么問題? 很顯然迅箩,這樣設計起來很簡單方便溉愁,但是所有的屬性外部都能訪問修改,非常不安全饲趋。 如何改進呢拐揭?

改進一 隱藏實現(xiàn)細節(jié)

對于帳戶的信息撤蟆,特別是金額,這是不能夠讓用戶直接修改的堂污,如果要改變信息家肯,需要窗口去辦理。

程序實現(xiàn)也是一樣盟猖,在使用對象時讨衣,盡量不要讓使用者直接操作對象中的屬性,這樣會帶來安全隱患式镐。

改進辦法反镇,使用私有屬性。

class Account(object):

def __init__(self, name, money):

self.__name = name? ? # 帳戶人姓名

self.__balance = money? ? # 帳戶余額

代碼改進以后碟案,將所有的屬性都設計成了對象私有屬性愿险,確實從外部在使用時,并不知道對象內部的屬性是什么价说,不能直接修改對象了辆亏,隱藏了實現(xiàn)的細節(jié)。

但是隨之又產生了另外一個問題鳖目,如果確實需要對這兩個屬性要進行修改怎么辦呢扮叨?

改進二 提供精確的訪問控制

在之前的學習中,學習過 set/get方法领迈,是專門來為類的私有屬性提供訪問接口彻磁。

class Account(object):

? ? def __init__(self, name, money):

? ? ? ? self.__name = name? ? # 帳戶人姓名

? ? ? ? self.__balance = money? ? # 帳戶余額

? ? # 帳戶人姓名,在創(chuàng)建帳戶時就已經確定狸捅,不允許修改衷蜓,所以對外不提供姓名的 set 方法

? ? def get_name(self):

? ? ? ? return self.__name

? ? def set_balance(self,money):

? ? ? ? self.__balance = money

? ? def get_balance(self):

? ? ? ? return self.__balance

經過修改,外部使用這個類的對象時尘喝,想使用對象中的屬性磁浇,只能通過類中提供的 set/get 接口來操作,提高了程序的安全性朽褪。

這樣置吓,程序基本達到了設計需求,但是能不能更加完善呢缔赠?

如果在使用這個類的對象過程中衍锚,由于誤操作,傳入了不正常的數據嗤堰,導致數據異常戴质。該如何以避免這種情況發(fā)生呢?

比如:設置金額時出現(xiàn)了負數,或字符串置森,或其它類型的對象斗埂。

改進三 保證數據有效性

在 set 方法中符糊,對傳入的數據進行判斷有效性凫海,如果是無效數據,提示用戶出錯男娄。

class Account(object):

? ? def __init__(self, name, money):

? ? ? ? self.__name = name? ? # 帳戶人姓名

? ? ? ? self.__balance = money? ? # 帳戶余額

? ? def get_name(self):

? ? ? ? return self.__name

? ? def set_balance(self,money):

? ? ? ? if isinstance(money, int):

? ? ? ? ? ? if money >= 0:

? ? ? ? ? ? ? ? self.__balance = money

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? raise ValueError('輸入的金額不正確')

? ? ? ? else:

? ? ? ? ? ? raise ValueError('輸入的金額不是數字')

? ? def get_balance(self):

? ? ? ? return self.__balance

經過幾個版本的迭代行贪,程序越來越健壯。安全性也越來越高模闲。

但是在使用的過程中建瘫,能不能更加精練一些呢?即然 set/get 方法是用來操作屬性的方法尸折,那么能不能以屬性操作的方式來使用呢啰脚?

答案是肯定的。

1.5.4 property 類

在 Python 中实夹,提供了一個叫做 property 的類橄浓,通過對這個創(chuàng)建這個類的對象的設置,在使用對象的私有屬性時亮航,可以不在使用屬性的函數的調用方式荸实,而像普通的公有屬性一樣去使用屬性,為開發(fā)提供便利缴淋。

property(fget=None, fset=None, fdel=None, doc=None) -> property attribute

property 是一個類准给,init方法由四個參數組成,實例后返回一個用來操作屬性的對象 參數一:屬性的獲取方法 參數二:屬性的設置方法 參數三:屬性的刪除方法 參數四:屬性的描述

class Account(object):

? ? def __init__(self, name, money):

? ? ? ? self.__name = name? ? # 帳戶人姓名

? ? ? ? self.__balance = money? ? # 帳戶余額

? ? def __get_name(self):

? ? ? ? return self.__name

? ? def set_balance(self,money):

? ? ? ? if isinstance(money, int):

? ? ? ? ? ? if money >= 0:

? ? ? ? ? ? ? ? self.__balance = money

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? raise ValueError('輸入的金額不正確')

? ? ? ? else:

? ? ? ? ? ? raise ValueError('輸入的金額不是數字')

? ? def get_balance(self):

? ? ? ? return self.__balance

? ? # 使用 property 類來為屬性設置便利的訪問方式

? ? name = property(__get_name)

? ? balance = property(get_balance, set_balance)

ac = Account('tom', 10)

print(ac.name)

print(ac.balance)

ac.balance = 1000

print(ac.balance)

通過 property 類實例對象以后重抖,在使用對象中的屬性時露氮,就可以像使用普通公有屬性一樣來調用,但是實際調用的還是 set/get 方法钟沛。 在實例 property 對象時畔规,不是所有的參數都需要寫,比如示例中的 name 只提供了 get 方法讹剔,并且是一個私有的方法油讯。這樣就完全隱藏了內部的實現(xiàn)細節(jié) 。

1.5.5 @property 裝飾器

Python 語法中延欠,提供一種裝飾器語法陌兑,在函數定義的上一行,使用 @xxx 的形式來使用裝飾器由捎。

裝飾器的作用就是提供裝飾的功能兔综,在不改變原來函數功能的基礎上,添加新的功能。(裝飾器語法會在后面的課程中單獨講解)

這種形式被稱為語法糖软驰。

語法糖指那些沒有給計算機語言添加新功能涧窒,而只是對人類來說更“甜蜜”的語法。 語法糖往往給程序員提供了更實用的編碼方式锭亏,有益于更好的編碼風格纠吴,更易讀。

利用 @property 裝飾器慧瘤,可以用來簡化定義新的屬性或修改現(xiàn)有的操作戴已。

class Account(object):

? ? def __init__(self, name, money):

? ? ? ? self.__name = name? ? # 帳戶人姓名

? ? ? ? self.__balance = money? ? # 帳戶余額

? ? # property 只能對獲取方法裝飾,并且獲取方法不需要再寫 get

? ? @property

? ? def name(self):

? ? ? ? return self.__name

? ? # 如果 property 裝飾的屬性還有 set 方法锅减,需要寫到 get方法后定義

? ? @property

? ? def balance(self):

? ? ? ? return self.__balance

? ? # 實現(xiàn) set 方法糖儡, 格式: @xxx.setter ,xxx 要和property裝飾的方法名一致

? ? @balance.setter

? ? def balance(self, money):

? ? ? ? if isinstance(money, int):

? ? ? ? ? ? if money >= 0:

? ? ? ? ? ? ? ? self.__balance = money

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? raise ValueError('輸入的金額不正確')

? ? ? ? else:

? ? ? ? ? ? raise ValueError('輸入的金額不是數字')

ac = Account('tom', 10)

print(ac.name)

print(ac.balance)

ac.balance = 1000

print(ac.balance)

注意:

? 在使用 @property 裝飾屬性時,只能裝飾獲取方法

? @property 裝飾屬性時怔匣, set/get 方法不需要再屬性名前加 set 和 get ,直接寫屬性名即可

? 如果一個屬性同時有 set/get 方法握联,那么要先實現(xiàn) @property 對獲取方法的定義

? 再實現(xiàn)設置方法的定義,定義時使用 @xxx.setter 裝飾每瞒,xxx 要和獲取方法名保持一致

1.5.5 總結

? 1. 在設計類時金闽,盡量使用私有屬性,然后使用 set/get 接口來提供讀寫操作

? 2. 使用 set/get 接口方法独泞,可以方便控制數據的有效性呐矾,精細化控制訪問權限,隱藏實現(xiàn)細節(jié)

? 3. 在定義 set/get 函數時懦砂,可以使用實例 property 類的對象蜒犯,或 使用 @property 裝飾器來對方法進行處理

? 4. 處理之后的 set/get 函數在使用時,可以像直接使用屬性一樣進行操作荞膘,但實際調用還是函數罚随,簡化操作

? 5. 一個類,一個是裝飾器

? 6. Python 中提供了很多語法糖羽资,語法糖的作用是用來簡化操作淘菩,使代碼開發(fā)更簡單,是一種對開發(fā)人員‘更甜蜜’語法

1.6 魔法屬性和魔法方法

學習目標

? 1. 能夠說出什么是魔法方法

? 2. 能夠說出魔法屬性的作用

? 3. 能夠說出不同的魔法方法調用的時機

--------------------------------------------------------------------------------

1.6.1 魔法屬性和魔法方法概述

在 Python 中預先定義好了一些以 __xxx__ 形式的屬性和方法屠升。

這些屬性和方法用來表示特定的意義和功能潮改。

在程序運行過程中自動調用,或者根據開發(fā)的需求手動調用腹暖,甚至重寫魔法方法的功能汇在。

本節(jié)主要介紹一些在開發(fā)中經常用到的魔法屬性和魔法方法。

1.6.2 __doc__ 屬性

Python 中有個特性叫做文檔字符串脏答,即DocString糕殉,這個特性可以讓你的程序文檔更加清晰易懂亩鬼。

DocString 通俗的說就是文件中的特殊注釋。用來描述文件阿蝶,類雳锋,函數等的功能。

DocString 有固定的格式羡洁,一般是放在自己函數定義的第一行玷过,用 ‘ ’ 符號指示,在這 ‘ ’ 里面添加函數功能說明就可以了焚廊。

DocString 可以使用 xxx.__doc__(注意前后都是雙_)屬性冶匹,將 DocString 特性 print 打印出來习劫。print(print.__doc__)

AA.py

''' This is File DocString '''

def display():

? ? ''' This is Display Function DocString. '''

? ? pass

class Test(object):

? ? ''' This is Test Class DocString! '''

? ? pass

? ? def show(self):

? ? ? ? ''' This is Show Function DocString '''

? ? ? ? pass

BB.py

import AA

t = AA.Test()

print(t.__doc__)? ? ? ? ? ? # 對象使用描述是當前對象所屬類的描述

print(AA.Test.__doc__)? ? ? # 類的描述

print(t.show.__doc__)? ? ? # 對象方法的描述咆瘟,查看方法描述時,方法名后面不能有括號

print(AA.display.__doc__)? # 公有函數的描述

print(AA.__doc__)? ? ? ? ? # 文件(模塊)的描述

Python的系統(tǒng)文件都在使用這個特性對功能進行描述诽里。

print(print.__doc__)

調用的就是 print 函數的第一行描述內容

help函數 DocSting的典型用法是使用 help()調用袒餐。

當使用 help 函數時,help函數會通過__doc__魔法屬性將參數中的 DocString 屬性展示出來谤狡。

def display():

? ? ''' This is Display Function DocString. '''

? ? pass

help(display)

程序運行結果:

Help on function display in module __main__:

display()

? ? This is Display Function DocString.

1.6.3 __module__ 屬性 灸眼、 __class__ 屬性 、__bases__ 屬性 墓懂、 __mro__ 屬性

在 Python 程序開發(fā)過程中經常會導入很多其它模塊或者創(chuàng)建很多類的實例對象焰宣。

并且,Python 是一個支持多繼承和多重繼承的編程語言捕仔。

多繼承是指一個類同時繼承多個類匕积。 多重繼承是指一個類所繼承的類之前還有繼承關系。

當使用模塊較多時榜跌,可以通過 __module__ 來查看當前成員屬于哪個模塊闪唆,通過 __class__ 屬性查看對象屬于哪個類

當類中有復雜的繼承關系時,可以通過 __bases__ 查看本類的父類有哪些钓葫,通過 __mro__ 屬性查看類中方法的查找順序悄蕾。

AA.py

# 動物類

class Animal(object):

? ? pass

# 人類繼承動物類

class Person(Animal):

? ? pass

# 鳥類繼承動物類

class Bird(Animal):

? ? pass

# 鳥人類繼承人類和鳥類,即是多繼承础浮,也是多重繼承

class BirdMan(Person, Bird):

? ? pass

# 顯示鳥人類的父類

print(BirdMan.__bases__)

# 顯示鳥人類初始化或實例讓用方法時的查找順序

print(BirdMan.__mro__)

BB.py

from AA import *

# 使用 module 查看當前類屬于哪個模塊

print(BirdMan.__module__)

# 使用 class 查看指定類的對象屬于哪個類

print(BirdMan().__class__)

# 使用 bases 來查看當前類的直接父類

print(BirdMan.__bases__)

# 使用 mro 來查看多重多繼承時的繼承關系

print(BirdMan.__mro__)

程序運行結果:

AA

<class 'AA.BirdMan'>

(<class 'AA.Person'>, <class 'AA.Bird'>)

(<class 'AA.BirdMan'>, <class 'AA.Person'>, <class 'AA.Bird'>, <class 'AA.Animal'>, <class 'object'>)

1.6.4 __new__ 方法 和 __init__ 方法

在 Python 中,__init__ 方法用來對實例中的屬性進行初始化操作帆调,在使用類創(chuàng)建實例對象時,就會自動調用這方法豆同。

但是 __init__ 方法并不是在創(chuàng)建對象時第一個被執(zhí)行的方法番刊。

在創(chuàng)建對象時,Pyhton 會先在內存中申請對象的內存空間诱告。如果申請成功撵枢,說明對象才創(chuàng)建成功民晒,之后才是使用 __init__ 進行初始化操作。

而申請對象空間這個過程就是由 __new__ 方法來執(zhí)行的锄禽。

也就是說在創(chuàng)建對象過程中潜必,會先執(zhí)行 __new__ 在內存中申請實例存儲空間,然后再執(zhí)行 __new__ 初始化實例對象空間沃但。

可以通過下面的代碼驗證:

class A(object):

? ? def __new__(cls, *args, **kwargs):

? ? ? ? print('New Run...')

? ? ? ? return super().__new__(cls, *args, **kwargs)

? ? def __init__(self):

? ? ? ? print('Init Run ...')

a = A()

程序運行結果:

New Run...

Init Run ...

__new__ 方法在開辟完內存空間后磁滚,會自動調用 __init__ 方法來初始化對象空間。

開發(fā)過程中宵晚,一般不會重寫 __new__ 方法育苟。一般都是重寫 __init__ 方法。

Python官方文檔的說法派任,__new__ 方法主要是當你繼承一些不可變的class時(比如int, str, tuple)赶诊, 提供給你一個自定義這些類的實例化過程的途徑。 比如逸贾,要繼承 int 類陨仅,實現(xiàn)一個永遠是整數的類。 另外铝侵,在實現(xiàn)單例類的時候可以使用 __new__ 灼伤。

1.6.5 __call__ 方法

需求:記錄一個函數執(zhí)行的次數

在程序設計過程中,并不建議使用全局變量咪鲜,因為會大量占用內存空間不釋放狐赡,會破壞程序的封裝性。 而且全局變量在使用過程中疟丙,任何人都可以訪問颖侄,不安全,不符合面向對象的封裝思想隆敢。

這樣就可以使用 __call__ 方法來實現(xiàn)這個需求

實現(xiàn)__call__后发皿,可以將對象當做函數一樣去使用,稱為仿函數或函數對象

那么普通函數和函數對象有什么區(qū)別拂蝎?

普通對象在執(zhí)行完成后就結束了穴墅,不能保存函數執(zhí)行的狀態(tài)。而且在擴展其它函數時温自,函數間的關聯(lián)性不強玄货。

函數對象可以保存函數的狀態(tài)。比如要實現(xiàn)對象調用的次數悼泌。而且可以在類中擴展更多的功能松捉。

class MyFun(object):

? ? def __init__(self):

? ? ? ? self.__call_num = 0

? ? def __call__(self, *args, **kwargs):

? ? ? ? print('MyFunc Run...')

? ? ? ? self.__call_num += 1

? ? def get_call_num(self):

? ? ? ? return self.__call_num

mf = MyFun()

mf()

mf()

print(mf.get_call_num())

程序運行結果 :

MyFunc Run...

MyFunc Run...

2

1.6.6 __getitem__ 、__setitem__ 馆里、__delitem__ 隘世、__len__ 方法

魔術方法的作用:

__getitem__(self,key):返回鍵對應的值可柿。

__setitem__(self,key,value):設置給定鍵的值

__delitem__(self,key):刪除給定鍵對應的元素。

__len__():返回元素的數量

當我們對類的屬性item進行下標的操作時丙者,會被__getitem__()/__setitem__()/__delitem__()攔截复斥,從而進行相應的操作,如賦值械媒,修改內容目锭,刪除內容等等。

如果現(xiàn)在要設計一個學生管理的類纷捞,實現(xiàn)學生信息的添加痢虹,獲取,刪除等操作主儡,應該怎么設計奖唯?

但是我們發(fā)現(xiàn),這個管理系統(tǒng)在管理學生時缀辩,實際使用的是一個字典臭埋,在管理學生信息的時候,能否不使用類提供的函數臀玄,而像使用字典一樣,直接通過操作字典的key的方式來直接操作呢畅蹂?

類似如下操作: d = {} d['one'] = 1 print(d['one'])

程序改進

class StudentManager(object):

? ? '''學生信息管理'''

? ? def __init__(self):

? ? ? ? # 使用字典來保存所有的學生信息

? ? ? ? self.__students = {}

? ? # 添加學生

? ? def __setitem__(self, key, value):

? ? ? ? self.__students[key] = value

? ? # 獲取學生

? ? def __getitem__(self, item):

? ? ? ? if item not in self.__students:

? ? ? ? ? ? return None

? ? ? ? return self.__students[item]

? ? # 刪除學生

? ? def __delitem__(self, key):

? ? ? ? if key in self.__students:

? ? ? ? ? ? del self.__students[key]

? ? # 獲取學生人數

? ? def __len__(self):

? ? ? ? return len(self.__students)

# 創(chuàng)建學生管理對象

sm = StudentManager()

# 添加兩個學生

sm[1] = 'tom'

sm[2] = 'jack'

# 查看學生個數

print(len(sm))

# 顯示添加的學生

print(sm[1])

print(sm[2])

# 刪除2號學生

del sm[2]

# 查看學生個數

print(len(sm))

# 查看是否刪除

print(sm[1])

print(sm[2])

運行結果 :

2

tom

jack

1

tom

None

可以看出健无,結果完全相同,但是使用更加簡潔液斜。這才是Python語法的精髓累贤。

在自定義對象可以使用 對象[ ] 形式來直接操作對象中的容器,使代碼書寫更加簡潔少漆,可讀性更高臼膏。

1.6.7 __str__ 方法

當使用print輸出對象的時候,只要自己在類中定義了__str__(self)方法示损,那么就會打印從在這個方法中return的數據

列表或字典在輸出時渗磅,通過直接打印列表或字典的名字,會直接打印出列表或字典中的內容检访。

那么自定義類會打印出來什么呢始鱼?

<__main__.StudentManager object="" at="" 0x103a120f0="">

那么自定義類,能不能像系統(tǒng)列表一樣脆贵,顯示所有的信息呢医清?

這個功能可以通過 __str__ 方法來實現(xiàn)

...

? ? def __str__(self):

? ? ? ? return '[' + " : ".join(self.__students.values()) + ']'

程序運行結果 :

[tom : jack]

小結:

__str__ 的作用是一個自定義的類型,在轉換成字符串類型時卖氨,程序是不知道怎么辦的的会烙,因為程序也不知道你倒底寫了什么负懦。

python提供 __str__ 這個方法,讓開發(fā)人員重寫這個方法柏腻,制定自定義對象轉換成字符串的規(guī)則密似。

1.6.8 總結

? 魔法屬性和魔法方法都是 Python 定義好的一些屬性或方法,用來實現(xiàn)一些特定的功能。

? 魔法屬性和魔法方法的主要作用是用來簡化 Python 的操作葫盼,在編寫代碼時讓使用方式更加簡潔残腌。

1.7 多重繼承和多繼承

學習目標

? 1. 能夠說出什么是繼承

? 2. 能夠說出繼承的作用

? 3. 能夠說出多重繼承的初始化過程

? 4. 能夠說出多繼承的初始化過程

? 5. 什么是鉆石繼承

--------------------------------------------------------------------------------

1.7.1 繼承概述

在面向對象程序開發(fā)中,繼承是一個非常重要的概念贫导。也是一個非常貼近現(xiàn)實繼承關系的一種思想抛猫。

通過繼承,可以讓子類去擁有父類的屬性和方法孩灯,而不必重新編寫相同的代碼闺金,并且可以在父類的基礎上添加新的屬性和功能。

在繼承的同時峰档,子類還可以重寫父類中的方法败匹,從而獲取與父類不同的功能,實現(xiàn)多態(tài)讥巡。

在 Python 中 所有的類都是存在繼承關系的掀亩,沒有顯示繼承的,都是默認繼承 object 類

1.7.2 繼承的作用

? 子類在繼承父類的同時可以向子類中添加新的屬性和功能欢顷,提高開發(fā)效率槽棍,避免代碼冗余。

? 實現(xiàn)多態(tài)抬驴。

1.7.3 繼承回顧

單重單繼承

通過繼承炼七,讓子類調用方法,如果子類存在就直接調用執(zhí)行布持,如果沒有豌拙,就會到父類中去查找,如果父類中有就執(zhí)行父類中的方法题暖,如果沒有就再去object 類中去查找按傅,如果父類中沒有就會報錯。

1.7.4 多重單繼承的初始化問題

在設計單類時芙委,初始化數據時逞敷,只需要重寫__init__方法即可,可是當有多個類發(fā)生關系時灌侣,類中的屬性該何進行初始化呢推捐?

在發(fā)生繼承時,子類會繼承父類的屬性的方法侧啼。

當在初始化時牛柒,子類只需要初始化自己擴展的屬性即可堪簿,父類中的屬性交給父類去初始化。

使用 父類名.__init__()的形式來調用父類的初始化方法

為什么要這么做呢皮壁?

? 父類中的屬性是私有的椭更,子類根本不知道父類里的屬性是什么。

? 子類也不知道父類在初始化操作過程中做了哪些工作蛾魄。

? 所以誰的內容就交給誰去執(zhí)行是最安全的虑瀑。

class Person():

? ? def __init__(self, name):

? ? ? ? self.__name = name

? ? ? ? print('Peron init...')

? ? def get_name(self):

? ? ? ? return self.__name

class Father(Person):

? ? def __init__(self,name, age):

? ? ? ? Person.__init__(self, name)

? ? ? ? self.__age = age

? ? ? ? print('Father init...')

? ? def get_age(self):

? ? ? ? return self.__age

class Son(Father):

? ? def __init__(self,name,age, gender):

? ? ? ? Father.__init__(self,name, age)

? ? ? ? self.__gender = gender

? ? ? ? print('Son init...')

? ? def get_gender(self):

? ? ? ? return self.__gender

s = Son('Tom', 18, '男')

print(s.get_name(),s.get_age(),s.get_gender())

程序執(zhí)行結果:

Peron init...

Father init...

Son init...

Tom 18 男

1.7.5 多重多繼承的初始化問題

多重多繼承時使用 父類名.__init__()的形式來調用父類的初始化方法時,因為 Father 類和 Mother 類都是繼承于 Person 類的滴须,在自己的初始化方法中舌狗,都執(zhí)行了父類的初始化方法,所以Person 類的 __init__ 方法被執(zhí)行了兩次扔水。

? 多重多繼承的初始化使用super 函數

? super(類名, self).__init__(*args)

在 Python 中痛侍,提供 Super 函數來解決多繼承時父類被重復初始化這個問題。

super函數的格式

super(CurrentClassName, self).__init__(*args, **kwargs)

class Person():

? ? def __init__(self, name):

? ? ? ? self.__name = name

? ? ? ? print('Peron init...')

? ? def get_name(self):

? ? ? ? return self.__name

class Father(Person):

? ? def __init__(self, age, *args):

? ? ? ? super(Father, self).__init__(*args)

? ? ? ? self.__age = age

? ? ? ? print('Father init...')

? ? def get_age(self):

? ? ? ? return self.__age

class Mother(Person):

? ? def __init__(self, job,*args):

? ? ? ? super(Mother, self).__init__(*args)

? ? ? ? self.__job = job

? ? ? ? print('Mother init...')

? ? def get_job(self):

? ? ? ? return self.__job

class Son(Father, Mother):

? ? def __init__(self, name, age, gender, job):

? ? ? ? super(Son, self).__init__(age, job,name)

? ? ? ? self.__gender = gender

? ? ? ? print('Son init...')

? ? def get_gender(self):

? ? ? ? return self.__gender

s = Son('Tom', 18, '男','老師')

print(s.get_name(),s.get_age(),s.get_gender(),s.get_job())

程序執(zhí)行結果:

Peron init...

Father init...

Mother init...

Son init...

Tom 18 男 老師

通過執(zhí)行結果來看魔市,使用 super 改進后的代碼只初始化了一次 Person 的初始化方法主届。

這種初始化方式稱為鉆石繼承(菱形繼承),如圖顯示 :

當程序使用了 super 函數以后待德,可以正常初始化君丁,是以__mro__方法解析順序為依據。

1.7.6 __mro__

mro Method Resolution Order 方法解析順序

類名.__mro__返回一個元組類型的繼承鏈磅网,來確定繼承的順序

在類出現(xiàn)繼承時谈截,每個類中都會保存一個當前類的繼承關系的表。

super 函數在執(zhí)行時涧偷,會在自己保存的這個繼承關系中去查找第一個參數,也就是當前的類名的下一個類是誰毙死。然后去調用下個類的初始化方法燎潮。

Python 中使用廣度優(yōu)先算法來決定繼承關系的排序。(廣度優(yōu)先算法可自行查找資料了解)

廣度優(yōu)先算法:橫向優(yōu)先

深度優(yōu)先算法:縱向優(yōu)先

MRO順序

print(Son.__mro__)

程序執(zhí)行結果 :

(<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class '__main__.Person'>, <class 'object'>)

1.7.7 super的簡化寫法

在初始化父類時扼倘,也可以使用 super().__init__() 函數來調用确封,簡化super函數的寫法。

這時再菊,super函數中沒有參數爪喘,還是能正確執(zhí)行,就是依照 __mro__ 中的順序來執(zhí)行的纠拔。

class Person():

? ? def __init__(self, name):

? ? ? ? self.__name = name

? ? ? ? print('Peron init...')

? ? def get_name(self):

? ? ? ? return self.__name

class Mother(Person):

? ? def __init__(self, name, age, job):

? ? ? ? # super(Mother, self).__init__(name, age)

? ? ? ? super().__init__(name,age)

? ? ? ? self.__job = job

? ? ? ? print('Mother init...')

? ? def get_job(self):

? ? ? ? return self.__job

class Father(Person):

? ? def __init__(self,name, age):

? ? ? ? # super(Father, self).__init__(name)

? ? ? ? super().__init__(name)

? ? ? ? self.__age = age

? ? ? ? print('Father init...')

? ? def get_age(self):

? ? ? ? return self.__age

class Son(Mother, Father):

? ? def __init__(self, name, age, gender, job):

? ? ? ? # super(Son, self).__init__(name, age, job)

? ? ? ? super().__init__(name,age,job)

? ? ? ? self.__gender = gender

? ? ? ? print('Son init...')

? ? def get_gender(self):

? ? ? ? return self.__gender

s = Son('Tom', 18, '男','老師')

print(s.get_name(),s.get_age(),s.get_gender(),s.get_job())

程序執(zhí)行結果:

Peron init...

Father init...

Mother init...

Son init...

Tom 18 男 老師

對象在調用方法時秉剑,也會依照這個順序來查找方法。

類在繼承時稠诲,繼承關系的書寫順序會影響 __mro__ 中的順序侦鹏。

class A():

? ? pass

class B(A):

? ? pass

class C(A):

? ? pass

class D(B,C):

? ? pass

class E(C,B):

? ? pass

print(D.__mro__)

print(E.__mro__)

程序執(zhí)行結果:

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

(<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

1.7.8 總結

? 繼承的思想是為了避免代碼的冗余诡曙,方便程序的擴展和復用

? 多重繼承是繼承關系中很多代(縱向)

? 多繼承是一個類可以同時繼承多個類(橫向)

? Python 中的類可以多重繼承,也可以多繼承

? Python 通過 __mro__ 來確定繼承的順序略水,使用的是廣度優(yōu)先算法(橫向優(yōu)先)

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末价卤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子渊涝,更是在濱河造成了極大的恐慌慎璧,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跨释,死亡現(xiàn)場離奇詭異胸私,居然都是意外死亡,警方通過查閱死者的電腦和手機煤傍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門盖文,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚯姆,你說我怎么就攤上這事五续。” “怎么了龄恋?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵疙驾,是天一觀的道長。 經常有香客問我郭毕,道長它碎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任显押,我火速辦了婚禮扳肛,結果婚禮上,老公的妹妹穿的比我還像新娘乘碑。我一直安慰自己挖息,他們只是感情好,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布兽肤。 她就那樣靜靜地躺著套腹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪资铡。 梳的紋絲不亂的頭發(fā)上电禀,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音笤休,去河邊找鬼尖飞。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的葫松。 我是一名探鬼主播瓦糕,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腋么!你這毒婦竟也來了咕娄?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤珊擂,失蹤者是張志新(化名)和其女友劉穎圣勒,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體摧扇,經...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡圣贸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了扛稽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吁峻。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖在张,靈堂內的尸體忽然破棺而出用含,到底是詐尸還是另有隱情,我是刑警寧澤帮匾,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布啄骇,位于F島的核電站,受9級特大地震影響瘟斜,放射性物質發(fā)生泄漏缸夹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一螺句、第九天 我趴在偏房一處隱蔽的房頂上張望虽惭。 院中可真熱鬧,春花似錦蛇尚、人聲如沸趟妥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亲雪,卻和暖如春勇凭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背义辕。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工虾标, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灌砖。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓璧函,卻偏偏與公主長得像傀蚌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蘸吓,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內容