關(guān)于類的繼承和靜態(tài)屬性

在這一節(jié)怀薛,結(jié)合之前學(xué)過的內(nèi)容,我們來看如何在Python中表示類之間的繼承關(guān)系豌鸡。

首先疹蛉,基于上一節(jié)的例子活箕,我們創(chuàng)建了三個文件:

  • Person.py
  • Employee.py
  • Main.py

現(xiàn)在,我們打開Employee.py可款,在其中給Person添加一個派生類Empolyee

from Person import Person

class Employee(Person):

在這里例子里育韩,有三點(diǎn)值得我們注意:

第一克蚂、為了在Employee.py中使用Person,我們在文件開始使用了from Person import Person筋讨。由于Person定義在Person.py中埃叭,因此from后面的模塊名是Person,從中悉罕,我們引入了class Person赤屋。雖然它們同名,大家要把它們分清楚壁袄。

第二类早、我們的類定義,要和import語句保持兩個空行嗜逻,這是Python中推薦的編碼習(xí)慣莺奔。

第三、Python中变泄,我們用(Person)這樣的形式表示基類。在這個括號里恼琼,我們可以用逗號分隔多個基類妨蛹。但經(jīng)驗(yàn)告訴我們,多重繼承在更多時候并不會像我們想象一樣正常工作晴竞。因此蛙卤,我們還是暫時只討論單一繼承的情況。

派生類的init方法

定義了派生類之后噩死,第一件事颤难,就是定義它的__init__方法,就像你已有的OO經(jīng)驗(yàn)一樣已维,在派生類的__init__方法里行嗤,我們要初始化派生類和基類兩部分內(nèi)容:

class Employee(Person):
    def __init__(self, work_id, name, age):
        Person.__init__(self, name, age)
        self.work_id = work_id

這里唯一要注意的是派生類調(diào)用基類__init__的方法,是通過類名調(diào)用的垛耳。然后栅屏,我們就可以這樣來創(chuàng)建派生類對象了。在Main.py中堂鲜,添加下面的代碼:

from Employee import Employee

mars = Employee(11, 'mars', 30)

既然有了派生類栈雳,繼續(xù)之前,我們介紹幾個判斷類關(guān)系的函數(shù)缔莲。要判斷某個類是否是另外一個類的派生類可以使用issubclass

print(issubclass(Employee, Person))  # True

它的第一個參數(shù)是派生類類型哥纫,第二個參數(shù)是基類類型,最后痴奏,返回一個boolean值蛀骇。要判斷一個對象是否是某個類的對象厌秒,可以使用isinstance

print(isinstance(mars, Person))    # True
print(isinstance(mars, Employee))  # True

它的第一個參數(shù)是類對象,第二個參數(shù)是要判斷所屬的類型松靡,同樣返回一個boolean值简僧。要注意的是,這兩個函數(shù)都直接傳給它們類型就好了雕欺,并不用給它們傳遞類型的字符串名稱岛马。

重寫類方法

在Python里,重寫基類方法和在派生類中定義方法是沒有任何區(qū)別的屠列,我們不用像Swift一樣使用override關(guān)鍵字啦逆,只要重定義了,就算是重寫了笛洛。

我們通過重寫幾個Python class中內(nèi)置的方法夏志,來理解這個過程。實(shí)際上苛让,之前我們已經(jīng)干過這個事了沟蔑,就是在派生類中重寫__init__。除此之外狱杰,class中還有以下可以重寫的方法瘦材。

理解reprstr

每個Python class都包含了兩個和類型的字符串表達(dá)方式有關(guān)的方法,叫做__repr____str__仿畸,默認(rèn)情況下食棕,它們返回的結(jié)果是相同的。在Main.py中错沽,添加下面的代碼:

print(mars.__repr__())
print(mars.__str__())

# <Employee.Employee object at 0x109563550>

執(zhí)行一下就會看到簿晓,它們返回的都是類似上面注釋中的結(jié)果。那么千埃,為什么要有兩個方法呢憔儿?也許你可以找到很多關(guān)于這個問題的討論。但在我看來镰禾,理解下面這兩點(diǎn)就足以讓你用對它們了:

  • 首先皿曲,__str__是通過調(diào)用__repr__實(shí)現(xiàn)的,因此吴侦,它的默認(rèn)實(shí)現(xiàn)確沒什么特別的用途屋休;
  • 其次,__repr__是給一個類型的開發(fā)者使用的备韧,它應(yīng)該包含關(guān)于類型的更多信息劫樟;而__str__的描述則是針對類型的使用者使用的,它應(yīng)該盡可能易讀、友好叠艳;

有了上面這兩個原則之后奶陈,我們就可以試著改寫__str__的實(shí)現(xiàn),讓它只返回類型名稱本身附较,例如這樣:

class Employee(Person):
    # ...

    def __str__(self):
        return "Employee"

稍后吃粒,我們還會看到更好的獲取類型名稱的方法,這里我們只是直接返回了Employee拒课。重新執(zhí)行下徐勃,就會發(fā)現(xiàn),此時__repr____str__的結(jié)果不同了早像。

'''
<Employee.Employee object at 0x10e388518>
Employee
'''

重定義對象的比較行為

除了重定義類型的表達(dá)方式之外僻肖,我們還可以明確指定兩個類對象的比較方法。在Python 2的時候卢鹦,這是通過重寫__cmp__方法實(shí)現(xiàn)的臀脏。但在Python 3中,已經(jīng)不提倡這么做了冀自。Python 3中提供了一組表意更明確的函數(shù):

  • __eq__: equal
  • __ne__: not equal
  • __lt__: less than
  • __le__: less equal
  • __gt__: greater than
  • __ge__: greater equal

首先來看揉稚,當(dāng)我們不定義這些方法的時候,比較兩個Employee對象熬粗,執(zhí)行的是比較對象引用的操作:

mars = Employee(11, 'mars', 30)
eleven = Employee(11, 'mars', 30)

print(mars == eleven)  # false

顯然窃植,marseleven引用的是不同的對象,因此這個比較結(jié)果肯定是False荐糜。這時,如果我們希望按照對象的值比較葛超,認(rèn)為只要姓名暴氏、年齡和工號相等,那么兩個Employee對象就相等绣张,就可以重寫__eq__方法:

class Employee(Person):
    # ...
    def __eq__(self, other):
        return self.name == other.name and \
               self.age == other.age and \
               self.work_id == other.work_id

可以看到答渔,__eq__有兩個參數(shù),self可以理解為==的左操作數(shù)侥涵,other==的右操作數(shù)沼撕。而它的比較邏輯,就是逐個比較Employee的每個屬性芜飘,很簡單务豺。

定義好__eq__之后,之前的比較結(jié)果嗦明,就變成True了笼沥。

定義Class attributes

在這一節(jié)最后,我們來看如何給class添加靜態(tài)屬性,在Python里奔浅,這叫做class attribute馆纳。

之前我們提到過,所有直接定義在__init__方法里的屬性汹桦,都是類對象屬性鲁驶,它們都是綁定在某個對象上的。如果我們把屬性定義在__init__外面舞骆,這個屬性就被所有的類對象共享了钥弯。例如,我們添加一個統(tǒng)計所有員工對象數(shù)量的counter

class Employee(Person):
    counter = 0

可以看到葛作,counter的初始化是在定義的時候完成的寿羞。這樣,counter就會被所有的類對象共享了赂蠢,我們可以在每次__init__方法被調(diào)用的時候绪穆,把它加1:

def __init__(self, work_id, name, age):
    Person.__init__(self, name, age)
    self.work_id = work_id
    Employee.counter += 1

此時,由于我們有marseleven兩個Employee對象虱岂,訪問counter的時候玖院,得到的值,就是2了:

mars = Employee(11, 'mars', 30)
eleven = Employee(11, 'mars', 30)

print(Employee.counter)  # 2

在上面的例子里可以看到看到第岖,訪問class attribute的時候难菌,我們要使用ClassName.Attribute這樣的形式。

看到這蔑滓,你可能會想郊酒,這個counter可以隨便被人修改啊,用它來計數(shù)并不靠譜键袱。沒錯燎窘,默認(rèn)情況下,class的所有的屬性和方法都是可以被外部訪問的蹄咖。如果我們要隱藏這個屬性褐健,可以在它前面使用兩個下劃線:

class Employee(Person):
    __counter = 0

    def __init__(self, work_id, name, age):
        Person.__init__(self, name, age)
        self.work_id = work_id
        Employee.__counter += 1

然后,在Main.py里澜汤,無論你用哪種方式訪問counter

print(Employee.counter)
print(Employee.__counter)

都會觸發(fā)AttributeError異常蚜迅,并提示我們對應(yīng)的counter屬性不存在。

不過俊抵,你也別太當(dāng)真谁不,因?yàn)檫@至多也就是一層設(shè)計意圖上的保障罷了,它只能提醒你自己徽诲,__counter只能用在Employee內(nèi)部拍谐。

因?yàn)橹虻蓿琍ython中有一個魔術(shù)前綴,就是一個下劃線加上類名轩拨,對于我們的例子來說践瓷,就是_Employee,通過它亡蓉,你就可以訪問到私有成員了晕翠。于是,在Main.py里砍濒,我們把代碼改成這樣:

print(Employee._Employee__counter)  # 2

就可以恢復(fù)執(zhí)行了淋肾,而且,讀寫均可 :-)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爸邢,一起剝皮案震驚了整個濱河市樊卓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杠河,老刑警劉巖碌尔,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異券敌,居然都是意外死亡唾戚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門待诅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叹坦,“玉大人,你說我怎么就攤上這事卑雁∧际椋” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵测蹲,是天一觀的道長锐膜。 經(jīng)常有香客問我,道長弛房,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任而柑,我火速辦了婚禮文捶,結(jié)果婚禮上有滑,老公的妹妹穿的比我還像新娘帚豪。我一直安慰自己,他們只是感情好蜜徽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布涩澡。 她就那樣靜靜地躺著顽耳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上射富,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天膝迎,我揣著相機(jī)與錄音,去河邊找鬼胰耗。 笑死限次,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柴灯。 我是一名探鬼主播卖漫,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赠群!你這毒婦竟也來了羊始?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤查描,失蹤者是張志新(化名)和其女友劉穎突委,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叹誉,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸯两,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了长豁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钧唐。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖匠襟,靈堂內(nèi)的尸體忽然破棺而出钝侠,到底是詐尸還是另有隱情,我是刑警寧澤酸舍,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布帅韧,位于F島的核電站,受9級特大地震影響啃勉,放射性物質(zhì)發(fā)生泄漏忽舟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一淮阐、第九天 我趴在偏房一處隱蔽的房頂上張望叮阅。 院中可真熱鬧,春花似錦泣特、人聲如沸浩姥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勒叠。三九已至兜挨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間眯分,已是汗流浹背拌汇。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颗搂,地道東北人担猛。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像丢氢,于是被迫代替她去往敵國和親傅联。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1/579文件IO文件介紹I/O流流的概念讀寫文件文件備份IO介紹大家應(yīng)該聽說過一句話:“好記性不如爛筆頭”疚察。不僅...
    清清子衿木子水心閱讀 1,434評論 0 1
  • 5繼承 5.1 類蒸走、超類和子類 重用部分代碼,并保留所有域貌嫡”茸ぃ“is-a”關(guān)系,用extends表示岛抄。 已存在的類被...
    我快要上天啦閱讀 777評論 1 3
  • 寫在前面的話 代碼中的# > 表示的是輸出結(jié)果 輸入 使用input()函數(shù) 用法 注意input函數(shù)輸出的均是字...
    FlyingLittlePG閱讀 2,764評論 0 8
  • 目錄 一别惦,Python 面向?qū)ο?二,Python 正則表達(dá)式 三夫椭,Python CGI編程 四掸掸,Python 操...
    weiwei_js閱讀 217評論 0 0
  • 要點(diǎn): 函數(shù)式編程:注意不是“函數(shù)編程”,多了一個“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍畈淝铩傩浴?..
    victorsungo閱讀 1,510評論 0 6