python基础系列:类
類(lèi)相關(guān)概念:
- 類(lèi)(Class):?用來(lái)描述具有相同的屬性和方法的對(duì)象的集合。它定義了該集合中每個(gè)對(duì)象所共有的屬性和方法。對(duì)象是類(lèi)的實(shí)例。
- 類(lèi)變量:類(lèi)變量在整個(gè)實(shí)例化的對(duì)象中是公用的。類(lèi)變量定義在類(lèi)中且在函數(shù)體之外。類(lèi)變量通常不作為實(shí)例變量使用。
- 數(shù)據(jù)成員:類(lèi)變量或者實(shí)例變量用于處理類(lèi)及其實(shí)例對(duì)象的相關(guān)的數(shù)據(jù)。
- 方法重寫(xiě):如果從父類(lèi)繼承的方法不能滿足子類(lèi)的需求,可以對(duì)其進(jìn)行改寫(xiě),這個(gè)過(guò)程叫方法的覆蓋(override),也稱(chēng)為方法的重寫(xiě)。
- 實(shí)例變量:定義在方法中的變量,只作用于當(dāng)前實(shí)例的類(lèi)。
- 繼承:即一個(gè)派生類(lèi)(derived class)繼承基類(lèi)(base class)的字段和方法。繼承也允許把一個(gè)派生類(lèi)的對(duì)象作為一個(gè)基類(lèi)對(duì)象對(duì)待。例如,有這樣一個(gè)設(shè)計(jì):一個(gè)Dog類(lèi)型的對(duì)象派生自Animal類(lèi),素以Dog也是一個(gè)Animal。
- 實(shí)例化:創(chuàng)建一個(gè)類(lèi)的實(shí)例,類(lèi)的具體對(duì)象。
- 方法:類(lèi)中定義的函數(shù)。
- 對(duì)象:通過(guò)類(lèi)定義的數(shù)據(jù)結(jié)構(gòu)實(shí)例。對(duì)象包括兩個(gè)數(shù)據(jù)成員(類(lèi)變量和實(shí)例變量)和方法
類(lèi)和實(shí)例
面向?qū)ο笞钪匾母拍罹褪穷?lèi)(Class)和實(shí)例(Instance),必須牢記類(lèi)是抽象的模板,比如Student類(lèi),而實(shí)例是根據(jù)類(lèi)創(chuàng)建出來(lái)的一個(gè)個(gè)具體的“對(duì)象”,每個(gè)對(duì)象都擁有相同的方法,但各自的數(shù)據(jù)可能不同。
仍以Student類(lèi)為例,在Python中,定義類(lèi)是通過(guò)class關(guān)鍵字:
class Student(object):passclass后面緊接著是類(lèi)名,即Student,類(lèi)名通常是大寫(xiě)開(kāi)頭的單詞,緊接著是(object),表示該類(lèi)是從哪個(gè)類(lèi)繼承下來(lái)的,繼承的概念我們后面再講,通常,如果沒(méi)有合適的繼承類(lèi),就使用object類(lèi),這是所有類(lèi)最終都會(huì)繼承的類(lèi)。
定義好了Student類(lèi),就可以根據(jù)Student類(lèi)創(chuàng)建出Student的實(shí)例,創(chuàng)建實(shí)例是通過(guò)類(lèi)名+()實(shí)現(xiàn)的:
>>> bart = Student() >>> bart <__main__.Student object at 0x10a67a590> >>> Student <class '__main__.Student'>可以看到,變量bart指向的就是一個(gè)Student的實(shí)例,后面的0x10a67a590是內(nèi)存地址,每個(gè)object的地址都不一樣,而Student本身則是一個(gè)類(lèi)。
可以自由地給一個(gè)實(shí)例變量綁定屬性,比如,給實(shí)例bart綁定一個(gè)name屬性:
>>> bart.name = 'Bart Simpson' >>> bart.name 'Bart Simpson'由于類(lèi)可以起到模板的作用,因此,可以在創(chuàng)建實(shí)例的時(shí)候,把一些我們認(rèn)為必須綁定的屬性強(qiáng)制填寫(xiě)進(jìn)去。通過(guò)定義一個(gè)特殊的__init__方法,在創(chuàng)建實(shí)例的時(shí)候,就把name,score等屬性綁上去:
class Student(object):def __init__(self, name, score): self.name = name self.score = score注意到__init__方法的第一個(gè)參數(shù)永遠(yuǎn)是self,表示創(chuàng)建的實(shí)例本身,因此,在__init__方法內(nèi)部,就可以把各種屬性綁定到self,因?yàn)閟elf就指向創(chuàng)建的實(shí)例本身。
有了__init__方法,在創(chuàng)建實(shí)例的時(shí)候,就不能傳入空的參數(shù)了,必須傳入與__init__方法匹配的參數(shù),但self不需要傳,Python解釋器自己會(huì)把實(shí)例變量傳進(jìn)去:
>>> bart = Student('Bart Simpson', 59) >>> bart.name 'Bart Simpson' >>> bart.score 59和普通的函數(shù)相比,在類(lèi)中定義的函數(shù)只有一點(diǎn)不同,就是第一個(gè)參數(shù)永遠(yuǎn)是實(shí)例變量self,并且,調(diào)用時(shí),不用傳遞該參數(shù)。除此之外,類(lèi)的方法和普通函數(shù)沒(méi)有什么區(qū)別,所以,你仍然可以用默認(rèn)參數(shù)、可變參數(shù)、關(guān)鍵字參數(shù)和命名關(guān)鍵字參數(shù)。
數(shù)據(jù)封裝
面向?qū)ο缶幊痰囊粋€(gè)重要特點(diǎn)就是數(shù)據(jù)封裝。在上面的Student類(lèi)中,每個(gè)實(shí)例就擁有各自的name和score這些數(shù)據(jù)。我們可以通過(guò)函數(shù)來(lái)訪問(wèn)這些數(shù)據(jù),比如打印一個(gè)學(xué)生的成績(jī):
>>> def print_score(std): ... print('%s: %s' % (std.name, std.score)) ... >>> print_score(bart) Bart Simpson: 59但是,既然Student實(shí)例本身就擁有這些數(shù)據(jù),要訪問(wèn)這些數(shù)據(jù),就沒(méi)有必要從外面的函數(shù)去訪問(wèn),可以直接在Student類(lèi)的內(nèi)部定義訪問(wèn)數(shù)據(jù)的函數(shù),這樣,就把“數(shù)據(jù)”給封裝起來(lái)了。這些封裝數(shù)據(jù)的函數(shù)是和Student類(lèi)本身是關(guān)聯(lián)起來(lái)的,我們稱(chēng)之為類(lèi)的方法:
class Student(object):def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score))要定義一個(gè)方法,除了第一個(gè)參數(shù)是self外,其他和普通函數(shù)一樣。要調(diào)用一個(gè)方法,只需要在實(shí)例變量上直接調(diào)用,除了self不用傳遞,其他參數(shù)正常傳入:
>>> bart.print_score() Bart Simpson: 59這樣一來(lái),我們從外部看Student類(lèi),就只需要知道,創(chuàng)建實(shí)例需要給出name和score,而如何打印,都是在Student類(lèi)的內(nèi)部定義的,這些數(shù)據(jù)和邏輯被“封裝”起來(lái)了,調(diào)用很容易,但卻不用知道內(nèi)部實(shí)現(xiàn)的細(xì)節(jié)。
封裝的另一個(gè)好處是可以給Student類(lèi)增加新的方法,比如get_grade:
class Student(object):...def get_grade(self): if self.score >= 90: return 'A' elif self.score >= 60: return 'B' else: return 'C'同樣的,get_grade方法可以直接在實(shí)例變量上調(diào)用,不需要知道內(nèi)部實(shí)現(xiàn)細(xì)節(jié):
# -*- coding: utf-8 -*-class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
小結(jié)
類(lèi)是創(chuàng)建實(shí)例的模板,而實(shí)例則是一個(gè)一個(gè)具體的對(duì)象,各個(gè)實(shí)例擁有的數(shù)據(jù)都互相獨(dú)立,互不影響;
方法就是與實(shí)例綁定的函數(shù),和普通函數(shù)不同,方法可以直接訪問(wèn)實(shí)例的數(shù)據(jù);
通過(guò)在實(shí)例上調(diào)用方法,我們就直接操作了對(duì)象內(nèi)部的數(shù)據(jù),但無(wú)需知道方法內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)。
和靜態(tài)語(yǔ)言不同,Python允許對(duì)實(shí)例變量綁定任何數(shù)據(jù),也就是說(shuō),對(duì)于兩個(gè)實(shí)例變量,雖然它們都是同一個(gè)類(lèi)的不同實(shí)例,但擁有的變量名稱(chēng)都可能不同:
>>> bart = Student('Bart Simpson', 59) >>> lisa = Student('Lisa Simpson', 87) >>> bart.age = 8 >>> bart.age 8 >>> lisa.age Traceback (most recent call last):File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'age'訪問(wèn)限制
在Class內(nèi)部,可以有屬性和方法,而外部代碼可以通過(guò)直接調(diào)用實(shí)例變量的方法來(lái)操作數(shù)據(jù),這樣,就隱藏了內(nèi)部的復(fù)雜邏輯。
但是,從前面Student類(lèi)的定義來(lái)看,外部代碼還是可以自由地修改一個(gè)實(shí)例的name、score屬性:
>>> bart = Student('Bart Simpson', 59) >>> bart.score 59 >>> bart.score = 99 >>> bart.score 99如果要讓內(nèi)部屬性不被外部訪問(wèn),可以把屬性的名稱(chēng)前加上兩個(gè)下劃線__,在Python中,實(shí)例的變量名如果以__開(kāi)頭,就變成了一個(gè)私有變量(private),只有內(nèi)部可以訪問(wèn),外部不能訪問(wèn),所以,我們把Student類(lèi)改一改:
class Student(object):def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score))改完后,對(duì)于外部代碼來(lái)說(shuō),沒(méi)什么變動(dòng),但是已經(jīng)無(wú)法從外部訪問(wèn)實(shí)例變量.__name和實(shí)例變量.__score了:
>>> bart = Student('Bart Simpson', 59) >>> bart.__name Traceback (most recent call last):File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute '__name'這樣就確保了外部代碼不能隨意修改對(duì)象內(nèi)部的狀態(tài),這樣通過(guò)訪問(wèn)限制的保護(hù),代碼更加健壯。
但是如果外部代碼要獲取name和score怎么辦?可以給Student類(lèi)增加get_name和get_score這樣的方法:
class Student(object):...def get_name(self): return self.__name def get_score(self): return self.__score如果又要允許外部代碼修改score怎么辦?可以再給Student類(lèi)增加set_score方法:
class Student(object):...def set_score(self, score): self.__score = score你也許會(huì)問(wèn),原先那種直接通過(guò)bart.score = 99也可以修改啊,為什么要定義一個(gè)方法大費(fèi)周折?因?yàn)樵诜椒ㄖ?#xff0c;可以對(duì)參數(shù)做檢查,避免傳入無(wú)效的參數(shù):
class Student(object):...def set_score(self, score): if 0 <= score <= 100: self.__score = score else: raise ValueError('bad score')需要注意的是,在Python中,變量名類(lèi)似__xxx__的,也就是以雙下劃線開(kāi)頭,并且以雙下劃線結(jié)尾的,是特殊變量,特殊變量是可以直接訪問(wèn)的,不是private變量,所以,不能用__name__、__score__這樣的變量名。
有些時(shí)候,你會(huì)看到以一個(gè)下劃線開(kāi)頭的實(shí)例變量名,比如_name,這樣的實(shí)例變量外部是可以訪問(wèn)的,但是,按照約定俗成的規(guī)定,當(dāng)你看到這樣的變量時(shí),意思就是,“雖然我可以被訪問(wèn),但是,請(qǐng)把我視為私有變量,不要隨意訪問(wèn)”。
雙下劃線開(kāi)頭的實(shí)例變量是不是一定不能從外部訪問(wèn)呢?其實(shí)也不是。不能直接訪問(wèn)__name是因?yàn)镻ython解釋器對(duì)外把__name變量改成了_Student__name,所以,仍然可以通過(guò)_Student__name來(lái)訪問(wèn)__name變量:
>>> bart._Student__name 'Bart Simpson'但是強(qiáng)烈建議你不要這么干,因?yàn)椴煌姹镜腜ython解釋器可能會(huì)把__name改成不同的變量名。
總的來(lái)說(shuō)就是,Python本身沒(méi)有任何機(jī)制阻止你干壞事,一切全靠自覺(jué)。
最后注意下面的這種錯(cuò)誤寫(xiě)法:
>>> bart = Student('Bart Simpson', 59) >>> bart.get_name() 'Bart Simpson' >>> bart.__name = 'New Name' # 設(shè)置__name變量! >>> bart.__name 'New Name'表面上看,外部代碼“成功”地設(shè)置了__name變量,但實(shí)際上這個(gè)__name變量和class內(nèi)部的__name變量不是一個(gè)變量!內(nèi)部的__name變量已經(jīng)被Python解釋器自動(dòng)改成了_Student__name,而外部代碼給bart新增了一個(gè)__name變量。不信試試:
>>> bart.get_name() # get_name()內(nèi)部返回self.__name 'Bart Simpson'練習(xí)
請(qǐng)把下面的Student對(duì)象的gender字段對(duì)外隱藏起來(lái),用get_gender()和set_gender()代替,并檢查參數(shù)有效性:
# -*- coding: utf-8 -*-class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
# 測(cè)試: bart = Student('Bart', 'male') if bart.get_gender() != 'male':print('測(cè)試失敗!') else:bart.set_gender('female')if bart.get_gender() != 'female':print('測(cè)試失敗!')else:print('測(cè)試成功!')
繼承和多態(tài)
在OOP程序設(shè)計(jì)中,當(dāng)我們定義一個(gè)class的時(shí)候,可以從某個(gè)現(xiàn)有的class繼承,新的class稱(chēng)為子類(lèi)(Subclass),而被繼承的class稱(chēng)為基類(lèi)、父類(lèi)或超類(lèi)(Base class、Super class)。
比如,我們已經(jīng)編寫(xiě)了一個(gè)名為Animal的class,有一個(gè)run()方法可以直接打印:
class Animal(object):def run(self): print('Animal is running...')當(dāng)我們需要編寫(xiě)Dog和Cat類(lèi)時(shí),就可以直接從Animal類(lèi)繼承:
class Dog(Animal):pass class Cat(Animal): pass對(duì)于Dog來(lái)說(shuō),Animal就是它的父類(lèi),對(duì)于Animal來(lái)說(shuō),Dog就是它的子類(lèi)。Cat和Dog類(lèi)似。
繼承有什么好處?最大的好處是子類(lèi)獲得了父類(lèi)的全部功能。由于Animial實(shí)現(xiàn)了run()方法,因此,Dog和Cat作為它的子類(lèi),什么事也沒(méi)干,就自動(dòng)擁有了run()方法:
dog = Dog() dog.run()cat = Cat() cat.run()運(yùn)行結(jié)果如下:
Animal is running... Animal is running...當(dāng)然,也可以對(duì)子類(lèi)增加一些方法,比如Dog類(lèi):
class Dog(Animal):def run(self): print('Dog is running...') def eat(self): print('Eating meat...')繼承的第二個(gè)好處需要我們對(duì)代碼做一點(diǎn)改進(jìn)。你看到了,無(wú)論是Dog還是Cat,它們r(jià)un()的時(shí)候,顯示的都是Animal is running...,符合邏輯的做法是分別顯示Dog is running...和Cat is running...,因此,對(duì)Dog和Cat類(lèi)改進(jìn)如下:
class Dog(Animal):def run(self): print('Dog is running...') class Cat(Animal): def run(self): print('Cat is running...')再次運(yùn)行,結(jié)果如下:
Dog is running... Cat is running...當(dāng)子類(lèi)和父類(lèi)都存在相同的run()方法時(shí),我們說(shuō),子類(lèi)的run()覆蓋了父類(lèi)的run(),在代碼運(yùn)行的時(shí)候,總是會(huì)調(diào)用子類(lèi)的run()。這樣,我們就獲得了繼承的另一個(gè)好處:多態(tài)。
要理解什么是多態(tài),我們首先要對(duì)數(shù)據(jù)類(lèi)型再作一點(diǎn)說(shuō)明。當(dāng)我們定義一個(gè)class的時(shí)候,我們實(shí)際上就定義了一種數(shù)據(jù)類(lèi)型。我們定義的數(shù)據(jù)類(lèi)型和Python自帶的數(shù)據(jù)類(lèi)型,比如str、list、dict沒(méi)什么兩樣:
a = list() # a是list類(lèi)型 b = Animal() # b是Animal類(lèi)型 c = Dog() # c是Dog類(lèi)型判斷一個(gè)變量是否是某個(gè)類(lèi)型可以用isinstance()判斷:
>>> isinstance(a, list) True >>> isinstance(b, Animal) True >>> isinstance(c, Dog) True看來(lái)a、b、c確實(shí)對(duì)應(yīng)著list、Animal、Dog這3種類(lèi)型。
但是等等,試試:
>>> isinstance(c, Animal) True看來(lái)c不僅僅是Dog,c還是Animal!
不過(guò)仔細(xì)想想,這是有道理的,因?yàn)镈og是從Animal繼承下來(lái)的,當(dāng)我們創(chuàng)建了一個(gè)Dog的實(shí)例c時(shí),我們認(rèn)為c的數(shù)據(jù)類(lèi)型是Dog沒(méi)錯(cuò),但c同時(shí)也是Animal也沒(méi)錯(cuò),Dog本來(lái)就是Animal的一種!
所以,在繼承關(guān)系中,如果一個(gè)實(shí)例的數(shù)據(jù)類(lèi)型是某個(gè)子類(lèi),那它的數(shù)據(jù)類(lèi)型也可以被看做是父類(lèi)。但是,反過(guò)來(lái)就不行:
>>> b = Animal() >>> isinstance(b, Dog) FalseDog可以看成Animal,但Animal不可以看成Dog。
要理解多態(tài)的好處,我們還需要再編寫(xiě)一個(gè)函數(shù),這個(gè)函數(shù)接受一個(gè)Animal類(lèi)型的變量:
def run_twice(animal):animal.run()animal.run()當(dāng)我們傳入Animal的實(shí)例時(shí),run_twice()就打印出:
>>> run_twice(Animal()) Animal is running... Animal is running...當(dāng)我們傳入Dog的實(shí)例時(shí),run_twice()就打印出:
>>> run_twice(Dog()) Dog is running... Dog is running...當(dāng)我們傳入Cat的實(shí)例時(shí),run_twice()就打印出:
>>> run_twice(Cat()) Cat is running... Cat is running...看上去沒(méi)啥意思,但是仔細(xì)想想,現(xiàn)在,如果我們?cè)俣x一個(gè)Tortoise類(lèi)型,也從Animal派生:
class Tortoise(Animal):def run(self): print('Tortoise is running slowly...')當(dāng)我們調(diào)用run_twice()時(shí),傳入Tortoise的實(shí)例:
>>> run_twice(Tortoise()) Tortoise is running slowly... Tortoise is running slowly...你會(huì)發(fā)現(xiàn),新增一個(gè)Animal的子類(lèi),不必對(duì)run_twice()做任何修改,實(shí)際上,任何依賴Animal作為參數(shù)的函數(shù)或者方法都可以不加修改地正常運(yùn)行,原因就在于多態(tài)。
多態(tài)的好處就是,當(dāng)我們需要傳入Dog、Cat、Tortoise……時(shí),我們只需要接收Animal類(lèi)型就可以了,因?yàn)镈og、Cat、Tortoise……都是Animal類(lèi)型,然后,按照Animal類(lèi)型進(jìn)行操作即可。由于Animal類(lèi)型有run()方法,因此,傳入的任意類(lèi)型,只要是Animal類(lèi)或者子類(lèi),就會(huì)自動(dòng)調(diào)用實(shí)際類(lèi)型的run()方法,這就是多態(tài)的意思:
對(duì)于一個(gè)變量,我們只需要知道它是Animal類(lèi)型,無(wú)需確切地知道它的子類(lèi)型,就可以放心地調(diào)用run()方法,而具體調(diào)用的run()方法是作用在Animal、Dog、Cat還是Tortoise對(duì)象上,由運(yùn)行時(shí)該對(duì)象的確切類(lèi)型決定,這就是多態(tài)真正的威力:調(diào)用方只管調(diào)用,不管細(xì)節(jié),而當(dāng)我們新增一種Animal的子類(lèi)時(shí),只要確保run()方法編寫(xiě)正確,不用管原來(lái)的代碼是如何調(diào)用的。這就是著名的“開(kāi)閉”原則:
對(duì)擴(kuò)展開(kāi)放:允許新增Animal子類(lèi);
對(duì)修改封閉:不需要修改依賴Animal類(lèi)型的run_twice()等函數(shù)。
繼承還可以一級(jí)一級(jí)地繼承下來(lái),就好比從爺爺?shù)桨职帧⒃俚絻鹤舆@樣的關(guān)系。而任何類(lèi),最終都可以追溯到根類(lèi)object,這些繼承關(guān)系看上去就像一顆倒著的樹(shù)。比如如下的繼承樹(shù):
┌───────────────┐│ object │└───────────────┘│┌────────────┴────────────┐│ │▼ ▼┌─────────────┐ ┌─────────────┐│ Animal │ │ Plant │└─────────────┘ └─────────────┘│ │┌─────┴──────┐ ┌─────┴──────┐│ │ │ │▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Cat │ │ Tree │ │ Flower │ └─────────┘ └─────────┘ └─────────┘ └─────────┘靜態(tài)語(yǔ)言 vs 動(dòng)態(tài)語(yǔ)言
對(duì)于靜態(tài)語(yǔ)言(例如Java)來(lái)說(shuō),如果需要傳入Animal類(lèi)型,則傳入的對(duì)象必須是Animal類(lèi)型或者它的子類(lèi),否則,將無(wú)法調(diào)用run()方法。
對(duì)于Python這樣的動(dòng)態(tài)語(yǔ)言來(lái)說(shuō),則不一定需要傳入Animal類(lèi)型。我們只需要保證傳入的對(duì)象有一個(gè)run()方法就可以了:
class Timer(object):def run(self): print('Start...')這就是動(dòng)態(tài)語(yǔ)言的“鴨子類(lèi)型”,它并不要求嚴(yán)格的繼承體系,一個(gè)對(duì)象只要“看起來(lái)像鴨子,走起路來(lái)像鴨子”,那它就可以被看做是鴨子。
Python的“file-like object“就是一種鴨子類(lèi)型。對(duì)真正的文件對(duì)象,它有一個(gè)read()方法,返回其內(nèi)容。但是,許多對(duì)象,只要有read()方法,都被視為“file-like object“。許多函數(shù)接收的參數(shù)就是“file-like object“,你不一定要傳入真正的文件對(duì)象,完全可以傳入任何實(shí)現(xiàn)了read()方法的對(duì)象。
小結(jié)
繼承可以把父類(lèi)的所有功能都直接拿過(guò)來(lái),這樣就不必重零做起,子類(lèi)只需要新增自己特有的方法,也可以把父類(lèi)不適合的方法覆蓋重寫(xiě)。
動(dòng)態(tài)語(yǔ)言的鴨子類(lèi)型特點(diǎn)決定了繼承不像靜態(tài)語(yǔ)言那樣是必須的。
獲取對(duì)象信息
當(dāng)我們拿到一個(gè)對(duì)象的引用時(shí),如何知道這個(gè)對(duì)象是什么類(lèi)型、有哪些方法呢?
使用type()
首先,我們來(lái)判斷對(duì)象類(lèi)型,使用type()函數(shù):
基本類(lèi)型都可以用type()判斷:
>>> type(123) <class 'int'> >>> type('str') <class 'str'> >>> type(None) <type(None) 'NoneType'>如果一個(gè)變量指向函數(shù)或者類(lèi),也可以用type()判斷:
>>> type(abs) <class 'builtin_function_or_method'> >>> type(a) <class '__main__.Animal'>但是type()函數(shù)返回的是什么類(lèi)型呢?它返回對(duì)應(yīng)的Class類(lèi)型。如果我們要在if語(yǔ)句中判斷,就需要比較兩個(gè)變量的type類(lèi)型是否相同:
>>> type(123)==type(456) True >>> type(123)==int True >>> type('abc')==type('123') True >>> type('abc')==str True >>> type('abc')==type(123) False判斷基本數(shù)據(jù)類(lèi)型可以直接寫(xiě)int,str等,但如果要判斷一個(gè)對(duì)象是否是函數(shù)怎么辦?可以使用types模塊中定義的常量:
>>> import types >>> def fn(): ... pass ... >>> type(fn)==types.FunctionType True >>> type(abs)==types.BuiltinFunctionType True >>> type(lambda x: x)==types.LambdaType True >>> type((x for x in range(10)))==types.GeneratorType True使用isinstance()
對(duì)于class的繼承關(guān)系來(lái)說(shuō),使用type()就很不方便。我們要判斷class的類(lèi)型,可以使用isinstance()函數(shù)。
我們回顧上次的例子,如果繼承關(guān)系是:
object -> Animal -> Dog -> Husky那么,isinstance()就可以告訴我們,一個(gè)對(duì)象是否是某種類(lèi)型。先創(chuàng)建3種類(lèi)型的對(duì)象:
>>> a = Animal() >>> d = Dog() >>> h = Husky()然后,判斷:
>>> isinstance(h, Husky) True沒(méi)有問(wèn)題,因?yàn)閔變量指向的就是Husky對(duì)象。
再判斷:
>>> isinstance(h, Dog) Trueh雖然自身是Husky類(lèi)型,但由于Husky是從Dog繼承下來(lái)的,所以,h也還是Dog類(lèi)型。換句話說(shuō),isinstance()判斷的是一個(gè)對(duì)象是否是該類(lèi)型本身,或者位于該類(lèi)型的父繼承鏈上。
因此,我們可以確信,h還是Animal類(lèi)型:
>>> isinstance(h, Animal) True同理,實(shí)際類(lèi)型是Dog的d也是Animal類(lèi)型:
>>> isinstance(d, Dog) and isinstance(d, Animal) True但是,d不是Husky類(lèi)型:
>>> isinstance(d, Husky) False能用type()判斷的基本類(lèi)型也可以用isinstance()判斷:
>>> isinstance('a', str) True >>> isinstance(123, int) True >>> isinstance(b'a', bytes) True并且還可以判斷一個(gè)變量是否是某些類(lèi)型中的一種,比如下面的代碼就可以判斷是否是list或者tuple:
>>> isinstance([1, 2, 3], (list, tuple)) True >>> isinstance((1, 2, 3), (list, tuple)) True使用dir()
如果要獲得一個(gè)對(duì)象的所有屬性和方法,可以使用dir()函數(shù),它返回一個(gè)包含字符串的list,比如,獲得一個(gè)str對(duì)象的所有屬性和方法:
>>> dir('ABC') ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']類(lèi)似__xxx__的屬性和方法在Python中都是有特殊用途的,比如__len__方法返回長(zhǎng)度。在Python中,如果你調(diào)用len()函數(shù)試圖獲取一個(gè)對(duì)象的長(zhǎng)度,實(shí)際上,在len()函數(shù)內(nèi)部,它自動(dòng)去調(diào)用該對(duì)象的__len__()方法,所以,下面的代碼是等價(jià)的:
>>> len('ABC') 3 >>> 'ABC'.__len__() 3我們自己寫(xiě)的類(lèi),如果也想用len(myObj)的話,就自己寫(xiě)一個(gè)__len__()方法:
>>> class MyDog(object): ... def __len__(self): ... return 100 ... >>> dog = MyDog() >>> len(dog) 100剩下的都是普通屬性或方法,比如lower()返回小寫(xiě)的字符串:
>>> 'ABC'.lower() 'abc'僅僅把屬性和方法列出來(lái)是不夠的,配合getattr()、setattr()以及hasattr(),我們可以直接操作一個(gè)對(duì)象的狀態(tài):
>>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject()緊接著,可以測(cè)試該對(duì)象的屬性:
>>> hasattr(obj, 'x') # 有屬性'x'嗎? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有屬性'y'嗎? False >>> setattr(obj, 'y', 19) # 設(shè)置一個(gè)屬性'y' >>> hasattr(obj, 'y') # 有屬性'y'嗎? True >>> getattr(obj, 'y') # 獲取屬性'y' 19 >>> obj.y # 獲取屬性'y' 19如果試圖獲取不存在的屬性,會(huì)拋出AttributeError的錯(cuò)誤:
>>> getattr(obj, 'z') # 獲取屬性'z' Traceback (most recent call last):File "<stdin>", line 1, in <module> AttributeError: 'MyObject' object has no attribute 'z'可以傳入一個(gè)default參數(shù),如果屬性不存在,就返回默認(rèn)值:
>>> getattr(obj, 'z', 404) # 獲取屬性'z',如果不存在,返回默認(rèn)值404 404也可以獲得對(duì)象的方法:
>>> hasattr(obj, 'power') # 有屬性'power'嗎? True >>> getattr(obj, 'power') # 獲取屬性'power' <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn = getattr(obj, 'power') # 獲取屬性'power'并賦值到變量fn >>> fn # fn指向obj.power <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn() # 調(diào)用fn()與調(diào)用obj.power()是一樣的 81小結(jié)
通過(guò)內(nèi)置的一系列函數(shù),我們可以對(duì)任意一個(gè)Python對(duì)象進(jìn)行剖析,拿到其內(nèi)部的數(shù)據(jù)。要注意的是,只有在不知道對(duì)象信息的時(shí)候,我們才會(huì)去獲取對(duì)象信息。如果可以直接寫(xiě):
sum = obj.x + obj.y就不要寫(xiě):
sum = getattr(obj, 'x') + getattr(obj, 'y')一個(gè)正確的用法的例子如下:
def readImage(fp):if hasattr(fp, 'read'): return readData(fp) return None假設(shè)我們希望從文件流fp中讀取圖像,我們首先要判斷該fp對(duì)象是否存在read方法,如果存在,則該對(duì)象是一個(gè)流,如果不存在,則無(wú)法讀取。hasattr()就派上了用場(chǎng)。
請(qǐng)注意,在Python這類(lèi)動(dòng)態(tài)語(yǔ)言中,根據(jù)鴨子類(lèi)型,有read()方法,不代表該fp對(duì)象就是一個(gè)文件流,它也可能是網(wǎng)絡(luò)流,也可能是內(nèi)存中的一個(gè)字節(jié)流,但只要read()方法返回的是有效的圖像數(shù)據(jù),就不影響讀取圖像的功能。
實(shí)例屬性和類(lèi)屬性
閱讀: 186256由于Python是動(dòng)態(tài)語(yǔ)言,根據(jù)類(lèi)創(chuàng)建的實(shí)例可以任意綁定屬性。
給實(shí)例綁定屬性的方法是通過(guò)實(shí)例變量,或者通過(guò)self變量:
class Student(object):def __init__(self, name): self.name = name s = Student('Bob') s.score = 90但是,如果Student類(lèi)本身需要綁定一個(gè)屬性呢?可以直接在class中定義屬性,這種屬性是類(lèi)屬性,歸Student類(lèi)所有:
class Student(object):name = 'Student'當(dāng)我們定義了一個(gè)類(lèi)屬性后,這個(gè)屬性雖然歸類(lèi)所有,但類(lèi)的所有實(shí)例都可以訪問(wèn)到。來(lái)測(cè)試一下:
>>> class Student(object): ... name = 'Student' ... >>> s = Student() # 創(chuàng)建實(shí)例s >>> print(s.name) # 打印name屬性,因?yàn)閷?shí)例并沒(méi)有name屬性,所以會(huì)繼續(xù)查找class的name屬性 Student >>> print(Student.name) # 打印類(lèi)的name屬性 Student >>> s.name = 'Michael' # 給實(shí)例綁定name屬性 >>> print(s.name) # 由于實(shí)例屬性優(yōu)先級(jí)比類(lèi)屬性高,因此,它會(huì)屏蔽掉類(lèi)的name屬性 Michael >>> print(Student.name) # 但是類(lèi)屬性并未消失,用Student.name仍然可以訪問(wèn) Student >>> del s.name # 如果刪除實(shí)例的name屬性 >>> print(s.name) # 再次調(diào)用s.name,由于實(shí)例的name屬性沒(méi)有找到,類(lèi)的name屬性就顯示出來(lái)了 Student從上面的例子可以看出,在編寫(xiě)程序的時(shí)候,千萬(wàn)不要對(duì)實(shí)例屬性和類(lèi)屬性使用相同的名字,因?yàn)橄嗤Q(chēng)的實(shí)例屬性將屏蔽掉類(lèi)屬性,但是當(dāng)你刪除實(shí)例屬性后,再使用相同的名稱(chēng),訪問(wèn)到的將是類(lèi)屬性。
練習(xí)
為了統(tǒng)計(jì)學(xué)生人數(shù),可以給Student類(lèi)增加一個(gè)類(lèi)屬性,每創(chuàng)建一個(gè)實(shí)例,該屬性自動(dòng)增加:
# -*- coding: utf-8 -*-class Student(object):
count = 0
def __init__(self, name):
self.name = name
# 測(cè)試: if Student.count != 0:print('測(cè)試失敗!') else:bart = Student('Bart')if Student.count != 1:print('測(cè)試失敗!')else:lisa = Student('Bart')if Student.count != 2:print('測(cè)試失敗!')else:print('Students:', Student.count)print('測(cè)試通過(guò)!')
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
面向?qū)ο笕筇匦?/h3>
面向?qū)ο蟮娜筇匦允侵?#xff1a;封裝、繼承和多態(tài)。
一、封裝
封裝,顧名思義就是將內(nèi)容封裝到某個(gè)地方,以后再去調(diào)用被封裝在某處的內(nèi)容。
所以,在使用面向?qū)ο蟮姆庋b特性時(shí),需要:
- 將內(nèi)容封裝到某處
- 從某處調(diào)用被封裝的內(nèi)容
?
?self 是一個(gè)形式參數(shù),當(dāng)執(zhí)行 obj1 = Foo('wupeiqi', 18 ) 時(shí),self 等于 obj1
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??當(dāng)執(zhí)行 obj2 = Foo('alex', 78 ) 時(shí),self 等于 obj2
所以,內(nèi)容其實(shí)被封裝到了對(duì)象 obj1 和 obj2 中,每個(gè)對(duì)象中都有 name 和 age 屬性,在內(nèi)存里類(lèi)似于下圖來(lái)保存。
第二步:從某處調(diào)用被封裝的內(nèi)容
第二步:從某處調(diào)用被封裝的內(nèi)容
調(diào)用被封裝的內(nèi)容時(shí),有兩種情況:
- 通過(guò)對(duì)象直接調(diào)用
- 通過(guò)self間接調(diào)用
1、通過(guò)對(duì)象直接調(diào)用被封裝的內(nèi)容
上圖展示了對(duì)象 obj1 和 obj2 在內(nèi)存中保存的方式,根據(jù)保存格式可以如此調(diào)用被封裝的內(nèi)容:對(duì)象.屬性名
2、通過(guò)self間接調(diào)用被封裝的內(nèi)容
執(zhí)行類(lèi)中的方法時(shí),需要通過(guò)self間接調(diào)用被封裝的內(nèi)容
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef detail(self):print(self.name)print(self.age)obj1 = Foo('chengd', 18) obj1.detail() # Python默認(rèn)會(huì)將obj1傳給self參數(shù),即:obj1.detail(obj1),所以,此時(shí)方法內(nèi)部的 self = obj1,即:self.name 是 chengd ;self.age 是 18obj2 = Foo('python', 99) obj2.detail() # Python默認(rèn)會(huì)將obj2傳給self參數(shù),即:obj1.detail(obj2),所以,此時(shí)方法內(nèi)部的 self = obj2,即:self.name 是 python ; self.age 是 99x 執(zhí)行結(jié)果:?
?
綜上所述,對(duì)于面向?qū)ο蟮姆庋b來(lái)說(shuō),其實(shí)就是使用構(gòu)造方法將內(nèi)容封裝到 對(duì)象 中,然后通過(guò)對(duì)象直接或者self間接獲取被封裝的內(nèi)容。
| 練習(xí)二:游戲人生程序 1、創(chuàng)建三個(gè)游戲人物,分別是:
2、游戲場(chǎng)景,分別:
|
?
?
?
?
?
?
?
?
?
# -*- coding:utf-8 -*-
# ##################### 定義實(shí)現(xiàn)功能的類(lèi) #####################
class Person:
def __init__(self, na, gen, age, fig):
self.name = na
self.gender = gen
self.age = age
self.fight =fig
def grassland(self):
"""注釋:草叢戰(zhàn)斗,消耗200戰(zhàn)斗力"""
self.fight = self.fight - 200
def practice(self):
"""注釋:自我修煉,增長(zhǎng)100戰(zhàn)斗力"""
self.fight = self.fight + 200
def incest(self):
"""注釋:多人游戲,消耗500戰(zhàn)斗力"""
self.fight = self.fight - 500
def detail(self):
"""注釋:當(dāng)前對(duì)象的詳細(xì)情況"""
temp = "姓名:%s ; 性別:%s ; 年齡:%s ; 戰(zhàn)斗力:%s" % (self.name, self.gender, self.age, self.fight)
print temp
# ##################### 開(kāi)始游戲 #####################
cang = Person('蒼井井', '女', 18, 1000) # 創(chuàng)建蒼井井角色
dong = Person('東尼木木', '男', 20, 1800) # 創(chuàng)建東尼木木角色
bo = Person('波多多', '女', 19, 2500) # 創(chuàng)建波多多角色
cang.incest() #蒼井空參加一次多人游戲
dong.practice()#東尼木木自我修煉了一次
bo.grassland() #波多多參加一次草叢戰(zhàn)斗
#輸出當(dāng)前所有人的詳細(xì)情況
cang.detail()
dong.detail()
bo.detail()
cang.incest() #蒼井空又參加一次多人游戲
dong.incest() #東尼木木也參加了一個(gè)多人游戲
bo.practice() #波多多自我修煉了一次
#輸出當(dāng)前所有人的詳細(xì)情況
cang.detail()
dong.detail()
bo.detail()
游戲人生
二、繼承
繼承,面向?qū)ο笾械睦^承和現(xiàn)實(shí)生活中的繼承相同,即:子可以繼承父的內(nèi)容。
例如:
貓可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我們要分別為貓和狗創(chuàng)建一個(gè)類(lèi),那么就需要為 貓 和 狗 實(shí)現(xiàn)他們所有的功能,如下所示:
class 貓:
def 喵喵叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
class 狗:
def 汪汪叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
偽代碼
class 貓:def 喵喵叫(self):print '喵喵叫'def 吃(self):# do somethingdef 喝(self):# do somethingdef 拉(self):# do somethingdef 撒(self):# do somethingclass 狗:def 汪汪叫(self):print '喵喵叫'def 吃(self):# do somethingdef 喝(self):# do somethingdef 拉(self):# do somethingdef 撒(self):# do something偽代碼
上述代碼不難看出,吃、喝、拉、撒是貓和狗都具有的功能,而我們卻分別的貓和狗的類(lèi)中編寫(xiě)了兩次。如果使用 繼承 的思想,如下實(shí)現(xiàn):
動(dòng)物:吃、喝、拉、撒
? 貓:喵喵叫(貓繼承動(dòng)物的功能)
? 狗:汪汪叫(狗繼承動(dòng)物的功能)
class 動(dòng)物:
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
# 在類(lèi)后面括號(hào)中寫(xiě)入另外一個(gè)類(lèi)名,表示當(dāng)前類(lèi)繼承另外一個(gè)類(lèi)
class 貓(動(dòng)物):
def 喵喵叫(self):
print '喵喵叫'
# 在類(lèi)后面括號(hào)中寫(xiě)入另外一個(gè)類(lèi)名,表示當(dāng)前類(lèi)繼承另外一個(gè)類(lèi)
class 狗(動(dòng)物):
def 汪汪叫(self):
print '喵喵叫'
偽代碼
?
?
?
class Animal:
def eat(self):
print "%s 吃 " %self.name
def drink(self):
print "%s 喝 " %self.name
def shit(self):
print "%s 拉 " %self.name
def pee(self):
print "%s 撒 " %self.name
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '貓'
def cry(self):
print '喵喵叫'
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed = '狗'
def cry(self):
print '汪汪叫'
# ######### 執(zhí)行 #########
c1 = Cat('小白家的小黑貓')
c1.eat()
c2 = Cat('小黑的小白貓')
c2.drink()
d1 = Dog('胖子家的小瘦狗')
d1.eat()
代碼實(shí)例
代碼實(shí)例
所以,對(duì)于面向?qū)ο蟮睦^承來(lái)說(shuō),其實(shí)就是將多個(gè)類(lèi)共有的方法提取到父類(lèi)中,子類(lèi)僅需繼承父類(lèi)而不必一一實(shí)現(xiàn)每個(gè)方法。
注:除了子類(lèi)和父類(lèi)的稱(chēng)謂,你可能看到過(guò) 派生類(lèi) 和 基類(lèi) ,他們與子類(lèi)和父類(lèi)只是叫法不同而已。
?
學(xué)習(xí)了繼承的寫(xiě)法之后,我們用代碼來(lái)是上述阿貓阿狗的功能:
class Animal:
def eat(self):
print "%s 吃 " %self.name
def drink(self):
print "%s 喝 " %self.name
def shit(self):
print "%s 拉 " %self.name
def pee(self):
print "%s 撒 " %self.name
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '貓'
def cry(self):
print '喵喵叫'
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed = '狗'
def cry(self):
print '汪汪叫'
# ######### 執(zhí)行 #########
c1 = Cat('小白家的小黑貓')
c1.eat()
c2 = Cat('小黑的小白貓')
c2.drink()
d1 = Dog('胖子家的小瘦狗')
d1.eat()
代碼實(shí)例
代碼實(shí)例
那么問(wèn)題又來(lái)了,多繼承呢?
- 是否可以繼承多個(gè)類(lèi)
- 如果繼承的多個(gè)類(lèi)每個(gè)類(lèi)中都定了相同的函數(shù),那么那一個(gè)會(huì)被使用呢?
1、Python的類(lèi)可以繼承多個(gè)類(lèi),Java和C#中則只能繼承一個(gè)類(lèi)
2、Python的類(lèi)如果繼承了多個(gè)類(lèi),那么其尋找方法的方式有兩種,分別是:深度優(yōu)先和廣度優(yōu)先
下圖中B、C類(lèi)繼承D類(lèi),A類(lèi)繼承B、C類(lèi)。
?
- 當(dāng)類(lèi)是經(jīng)典類(lèi)時(shí),多繼承情況下,會(huì)按照深度優(yōu)先方式查找
- 當(dāng)類(lèi)是新式類(lèi)時(shí),多繼承情況下,會(huì)按照廣度優(yōu)先方式查找
經(jīng)典類(lèi)和新式類(lèi),從字面上可以看出一個(gè)老一個(gè)新,新的必然包含了跟多的功能,也是之后推薦的寫(xiě)法,從寫(xiě)法上區(qū)分的話,如果?當(dāng)前類(lèi)或者父類(lèi)繼承了object類(lèi),那么該類(lèi)便是新式類(lèi),否則便是經(jīng)典類(lèi)。
?
class D:
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 執(zhí)行bar方法時(shí)
# 首先去A類(lèi)中查找,如果A類(lèi)中沒(méi)有,則繼續(xù)去B類(lèi)中找,如果B類(lèi)中么有,則繼續(xù)去D類(lèi)中找,如果D類(lèi)中么有,則繼續(xù)去C類(lèi)中找,如果還是未找到,則報(bào)錯(cuò)
# 所以,查找順序:A --> B --> D --> C
# 在上述查找bar方法的過(guò)程中,一旦找到,則尋找過(guò)程立即中斷,便不會(huì)再繼續(xù)找了
a.bar()
經(jīng)典類(lèi)多繼承
經(jīng)典類(lèi)多繼承
class D:def bar(self):print 'D.bar'class C(D):def bar(self):print 'C.bar'class B(D):def bar(self):print 'B.bar'class A(B, C):def bar(self):print 'A.bar'a = A() # 執(zhí)行bar方法時(shí) # 首先去A類(lèi)中查找,如果A類(lèi)中沒(méi)有,則繼續(xù)去B類(lèi)中找,如果B類(lèi)中么有,則繼續(xù)去D類(lèi)中找,如果D類(lèi)中么有,則繼續(xù)去C類(lèi)中找,如果還是未找到,則報(bào)錯(cuò) # 所以,查找順序:A --> B --> D --> C # 在上述查找bar方法的過(guò)程中,一旦找到,則尋找過(guò)程立即中斷,便不會(huì)再繼續(xù)找了 a.bar()經(jīng)典類(lèi)多繼承
class D(object):
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 執(zhí)行bar方法時(shí)
# 首先去A類(lèi)中查找,如果A類(lèi)中沒(méi)有,則繼續(xù)去B類(lèi)中找,如果B類(lèi)中么有,則繼續(xù)去C類(lèi)中找,如果C類(lèi)中么有,則繼續(xù)去D類(lèi)中找,如果還是未找到,則報(bào)錯(cuò)
# 所以,查找順序:A --> B --> C --> D
# 在上述查找bar方法的過(guò)程中,一旦找到,則尋找過(guò)程立即中斷,便不會(huì)再繼續(xù)找了
a.bar()
新式類(lèi)多繼承
新式類(lèi)多繼承
class D(object):def bar(self):print 'D.bar'class C(D):def bar(self):print 'C.bar'class B(D):def bar(self):print 'B.bar'class A(B, C):def bar(self):print 'A.bar'a = A() # 執(zhí)行bar方法時(shí) # 首先去A類(lèi)中查找,如果A類(lèi)中沒(méi)有,則繼續(xù)去B類(lèi)中找,如果B類(lèi)中么有,則繼續(xù)去C類(lèi)中找,如果C類(lèi)中么有,則繼續(xù)去D類(lèi)中找,如果還是未找到,則報(bào)錯(cuò) # 所以,查找順序:A --> B --> C --> D # 在上述查找bar方法的過(guò)程中,一旦找到,則尋找過(guò)程立即中斷,便不會(huì)再繼續(xù)找了 a.bar()新式類(lèi)多繼承
經(jīng)典類(lèi):首先去A類(lèi)中查找,如果A類(lèi)中沒(méi)有,則繼續(xù)去B類(lèi)中找,如果B類(lèi)中么有,則繼續(xù)去D類(lèi)中找,如果D類(lèi)中么有,則繼續(xù)去C類(lèi)中找,如果還是未找到,則報(bào)錯(cuò)
新式類(lèi):首先去A類(lèi)中查找,如果A類(lèi)中沒(méi)有,則繼續(xù)去B類(lèi)中找,如果B類(lèi)中么有,則繼續(xù)去C類(lèi)中找,如果C類(lèi)中么有,則繼續(xù)去D類(lèi)中找,如果還是未找到,則報(bào)錯(cuò)
注意:在上述查找過(guò)程中,一旦找到,則尋找過(guò)程立即中斷,便不會(huì)再繼續(xù)找了
三、多態(tài)?
?Pyhon不支持Java和C#這一類(lèi)強(qiáng)類(lèi)型語(yǔ)言中多態(tài)的寫(xiě)法,但是原生多態(tài),其Python崇尚“鴨子類(lèi)型”。
class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
# 由于在Java或C#中定義函數(shù)參數(shù)時(shí),必須指定參數(shù)的類(lèi)型
# 為了讓Func函數(shù)既可以執(zhí)行S1對(duì)象的show方法,又可以執(zhí)行S2對(duì)象的show方法,所以,定義了一個(gè)S1和S2類(lèi)的父類(lèi)
# 而實(shí)際傳入的參數(shù)是:S1對(duì)象和S2對(duì)象
def Func(F1 obj):
"""Func函數(shù)需要接收一個(gè)F1類(lèi)型或者F1子類(lèi)的類(lèi)型"""
print obj.show()
s1_obj = S1()
Func(s1_obj) # 在Func函數(shù)中傳入S1類(lèi)的對(duì)象 s1_obj,執(zhí)行 S1 的show方法,結(jié)果:S1.show
s2_obj = S2()
Func(s2_obj) # 在Func函數(shù)中傳入Ss類(lèi)的對(duì)象 ss_obj,執(zhí)行 Ss 的show方法,結(jié)果:S2.show
Python偽代碼實(shí)現(xiàn)Java或C#的多態(tài)
Python偽代碼實(shí)現(xiàn)Java或C#的多態(tài)
class F1:passclass S1(F1):def show(self):print 'S1.show'class S2(F1):def show(self):print 'S2.show'# 由于在Java或C#中定義函數(shù)參數(shù)時(shí),必須指定參數(shù)的類(lèi)型 # 為了讓Func函數(shù)既可以執(zhí)行S1對(duì)象的show方法,又可以執(zhí)行S2對(duì)象的show方法,所以,定義了一個(gè)S1和S2類(lèi)的父類(lèi) # 而實(shí)際傳入的參數(shù)是:S1對(duì)象和S2對(duì)象def Func(F1 obj):"""Func函數(shù)需要接收一個(gè)F1類(lèi)型或者F1子類(lèi)的類(lèi)型"""print obj.show()s1_obj = S1() Func(s1_obj) # 在Func函數(shù)中傳入S1類(lèi)的對(duì)象 s1_obj,執(zhí)行 S1 的show方法,結(jié)果:S1.shows2_obj = S2() Func(s2_obj) # 在Func函數(shù)中傳入Ss類(lèi)的對(duì)象 ss_obj,執(zhí)行 Ss 的show方法,結(jié)果:S2.showPython偽代碼實(shí)現(xiàn)Java或C#的多態(tài)
class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
def Func(obj):
print obj.show()
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
Python “鴨子類(lèi)型”
Python “鴨子類(lèi)型”
class F1:passclass S1(F1):def show(self):print 'S1.show'class S2(F1):def show(self):print 'S2.show'def Func(obj):print obj.show()s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj) Python “鴨子類(lèi)型” ?
轉(zhuǎn)載于:https://www.cnblogs.com/lisonglin/p/10270787.html
總結(jié)
以上是生活随笔為你收集整理的python基础系列:类的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python pickle使用
- 下一篇: 个人如何开美容店 有哪些经验值得参考