日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python基础系列:类

發(fā)布時(shí)間:2025/5/22 python 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python基础系列:类 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

類(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):pass

class后面緊接著是類(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è)下劃線!!!

注意到__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'

lisa = Student('Lisa', 99) bart = Student('Bart', 59) print(lisa.name, lisa.get_grade()) print(bart.name, bart.get_grade())

小結(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) False

Dog可以看成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) True

h雖然自身是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 ?總是優(yōu)先使用isinstance()判斷類(lèi)型,可以將指定類(lèi)型及其子類(lèi)“一網(wǎng)打盡”。

使用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è)游戲人物,分別是:

  • 蒼井井,女,18,初始戰(zhàn)斗力1000
  • 東尼木木,男,20,初始戰(zhàn)斗力1800
  • 波多多,女,19,初始戰(zhàn)斗力2500

2、游戲場(chǎng)景,分別:

  • 草叢戰(zhàn)斗,消耗200戰(zhàn)斗力
  • 自我修煉,增長(zhǎng)100戰(zhàn)斗力
  • 多人游戲,消耗500戰(zhàn)斗力
# -*- 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 =figdef grassland(self): """注釋:草叢戰(zhàn)斗,消耗200戰(zhàn)斗力"""self.fight = self.fight - 200def practice(self): """注釋:自我修煉,增長(zhǎng)100戰(zhàn)斗力"""self.fight = self.fight + 200def incest(self): """注釋:多人游戲,消耗500戰(zhàn)斗力"""self.fight = self.fight - 500def 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()游戲人生

?

?

?

?

?

?

?

?

?

# -*- 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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产一国产精品一级毛片 | 精品国产乱码久久久久久影片 | 美女a视频 | 秋霞视频在线观看 | 久操视频在线观看免费 | 五月天色婷婷丁香 | 国产看真人毛片爱做a片 | 九七电影院97理论片 | 亚洲一区二区视频在线观看 | 久久影视网 | 中文人妻熟女乱又乱精品 | 伊人天堂网| 亚洲欧美激情另类校园 | 色国产在线 | 在线资源站 | 星空大象mv高清在线观看免费 | 天堂少妇| 无码精品一区二区免费 | 蜜臀麻豆 | 亚洲av成人精品一区二区三区 | 综合久久一区 | 91亚洲天堂 | 99久久99久久精品国产片果冰 | 蝌蚪网在线视频 | 久久久久女人精品毛片九一 | 久久国产一二三 | 伊人网国产 | 国产一级在线视频 | 五月天色丁香 | 国内自拍青青草 | 小罗莉极品一线天在线 | 亚洲天堂男 | 精品视频免费看 | 久久综合狠狠 | 少妇特黄a一区二区三区88av | 久久久久三级 | 久久奇米 | 性高潮久久久久久久久 | 亚洲熟伦熟女新五十路熟妇 | 91午夜影院 | 国产精品欧美大片 | 女同一区二区三区 | 青娱乐在线播放 | 国产在线中文字幕 | 国精品无码人妻一区二区三区 | 久久国产乱子伦免费精品 | 国产av无码专区亚洲av毛片搜 | 香蕉视频网站在线 | 亚洲一区偷拍 | 亚洲熟妇av日韩熟妇在线 | 韩国伦理片免费看 | 欧美日韩综合在线观看 | 国产乱淫精品一区二区三区毛片 | 亚洲情欲网 | 黄色免费网站在线 | 精品视频一二 | 国产欧美一区二区三区四区 | 久久久在线免费观看 | 偷拍亚洲另类 | 中文字幕av解说 | 国产精品69久久久久 | 91精品福利视频 | 亚洲欧美日韩综合在线 | 久久精品人人做人人爽 | 蜜臀av性久久久久蜜臀aⅴ | 国产精品日韩精品欧美精品 | 色av网站| 放荡闺蜜高h苏桃情事h | 少妇做爰k8经典 | 亚洲日本久久久 | 日日噜噜夜夜狠狠久久丁香五月 | 精品999www| 国内自拍av | 日本内谢少妇xxxxx少交 | 69精品无码成人久久久久久 | 国产精品电影一区二区 | 国产在线精品观看 | 人成在线 | 国产20页| 成人免费在线视频网站 | 看看黄色片| 亚洲一区二区在线免费观看 | 中文字幕在线观看日本 | 欧美亚洲黄色片 | 牛牛电影国产一区二区 | 总裁边开会边做小娇妻h | 亚洲人成影视 | 一本视频| 国产一线二线三线在线观看 | 成人片黄网站久久久免费 | 欧美无人区码suv | 男人和女人搞鸡 | 小蝌蚪视频色 | 日韩av高清 | 深爱激情久久 | 中国在线观看免费视频 | 狠狠操狠狠操 | 枫花恋在线观看 | 日本久久久久久久久久久 |