欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python入門(mén)第9/10頁(yè)

 更新時(shí)間:2007年02月08日 00:00:00   作者:  

第九章 類(lèi)
Python是一個(gè)真正面向?qū)ο蟮恼Z(yǔ)言,它只增加了很少的新語(yǔ)法就實(shí)現(xiàn)了類(lèi)。它的類(lèi)機(jī)制是C++ 和Modula-3的類(lèi)機(jī)制的混合。Python的類(lèi)并不嚴(yán)格限制用戶(hù)對(duì)定義的修改,它依賴(lài)于用戶(hù)自覺(jué)不去修改定義。然而Python對(duì)類(lèi)最重要的功能都保持了完全的威力。類(lèi)繼承機(jī)制允許多個(gè)基類(lèi)的繼承,導(dǎo)出類(lèi)可以重載基類(lèi)的任何方法,方法可以調(diào)用基類(lèi)的同名方法。對(duì)象可以包含任意多的私有數(shù)據(jù)。

用C++術(shù)語(yǔ)說(shuō),所有類(lèi)成員(包括數(shù)據(jù)成員)是公用的,所有成員函數(shù)是虛擬(virtual)的。沒(méi)有特別的構(gòu)建函數(shù)或銷(xiāo)毀函數(shù)(destructor)。如同在Modula-3中一樣,從對(duì)象的方法中要引用對(duì)象成員沒(méi)有簡(jiǎn)捷的辦法:方法函數(shù)的必須以對(duì)象作為第一個(gè)參數(shù),而在調(diào)用時(shí)則自動(dòng)提供。象在Smalltalk中一樣,類(lèi)本身也是對(duì)象,實(shí)際上這里對(duì)象的含義比較寬:在Python 中所有的數(shù)據(jù)類(lèi)型都是對(duì)象。象在C++或Modula-3中一樣,內(nèi)置類(lèi)型不能作為基類(lèi)由用戶(hù)進(jìn)行擴(kuò)展。并且,象C++但不象Modula-3,多數(shù)有特殊語(yǔ)法的內(nèi)置函數(shù)(如算術(shù)算符、下標(biāo)等)可以作為類(lèi)成員重定義。

9.1 關(guān)于術(shù)語(yǔ)
Python的對(duì)象概念比較廣泛,對(duì)象不一定非得是類(lèi)的實(shí)例,因?yàn)槿缤珻++和Modula-3而不同于Smalltalk,Python的數(shù)據(jù)類(lèi)型不都是類(lèi),比如基本內(nèi)置類(lèi)型整數(shù)、列表等不是類(lèi),甚至較古怪的類(lèi)型如文件也不是類(lèi)。然而,Python所有的數(shù)據(jù)類(lèi)型都或多或少地帶有一些類(lèi)似對(duì)象的語(yǔ)法。

對(duì)象是有單獨(dú)身份的,同一對(duì)象可以有多個(gè)名字與其聯(lián)系,這在其他語(yǔ)言中叫做別名。這樣做的好處乍一看并不明顯,而且對(duì)于非可變類(lèi)型(數(shù)字、字符串、序表(tuple))等沒(méi)有什么差別。但是別名句法對(duì)于包含可變對(duì)象如列表、字典及涉及程序外部物件如文件、窗口的程序有影響,這可以有利于程序編制,因?yàn)閯e名有些類(lèi)似指針:比如,傳遞一個(gè)對(duì)象變得容易,因?yàn)檫@只是傳遞了一個(gè)指針;如果一個(gè)函數(shù)修改了作為參數(shù)傳遞來(lái)的對(duì)象,修改結(jié)果可以傳遞回調(diào)用處。這樣就不必象Pascal那樣使用兩種參數(shù)傳遞機(jī)制。

9.2 Python作用域與名字空間
在引入類(lèi)之前,我們必須講一講Python的作用域規(guī)則。類(lèi)定義很好地利用了名字空間,需要了解Python如何處理作用域和名字空間才能充分理解類(lèi)的使用。另外,作用域規(guī)則也是一個(gè)高級(jí)Python程序員必須掌握的知識(shí)。

 先給出一些定義。 

名字空間是從名字到對(duì)象的映射。多數(shù)名字空間目前是用Python字典類(lèi)型實(shí)現(xiàn)的,不過(guò)這一點(diǎn)一般是注意不到的,而且將來(lái)可能會(huì)改變。下面是名字空間的一些實(shí)例:Python中內(nèi)置的名字(如abs()等函數(shù),以及內(nèi)置的例外名);模塊中的全局名;函數(shù)調(diào)用中的局部變量名。在某種意義上一個(gè)對(duì)象的所有屬性也構(gòu)成了一個(gè)名字空間。關(guān)于名字空間最重要的事要知道不同名字空間的名字沒(méi)有任何聯(lián)系;例如,兩個(gè)不同模塊可能都定義了一個(gè)叫“maximize ”的函數(shù)而不會(huì)引起混亂,因?yàn)槟K的用戶(hù)必須在函數(shù)名之前加上模塊名作為修飾。

另外,在Python中可以把任何一個(gè)在句點(diǎn)之后的名字稱(chēng)為屬性,例如,在表達(dá)式z.real中,real是一個(gè)對(duì)象z的屬性。嚴(yán)格地說(shuō),對(duì)模塊中的名字的引用是屬性引用:在表達(dá)式modname.funcname 中,modname是一個(gè)模塊對(duì)象,funcname是它的一個(gè)屬性。在這種情況下在模塊屬性與模塊定義的全局名字之間存在一個(gè)直接的映射:它們使用相同的名字空間!

屬性可以是只讀的也可以是可寫(xiě)的。在屬性可寫(xiě)的時(shí)候,可以對(duì)屬性賦值。模塊屬性是可寫(xiě)的:你可以寫(xiě)“modname.the_answer = 42”??蓪?xiě)屬性也可以用del語(yǔ)句閃出,如“del modname.the_answer”。

名字空間與不同時(shí)刻創(chuàng)建,有不同的生存周期。包含Python內(nèi)置名字的名字空間當(dāng)Python 解釋程序開(kāi)始時(shí)被創(chuàng)建,而且不會(huì)被刪除。模塊的全局名字空間當(dāng)模塊定義被讀入時(shí)創(chuàng)建,一般情況下模塊名字空間也一直存在到解釋程序退出。由解釋程序的最頂層調(diào)用執(zhí)行的語(yǔ)句,不論是從一個(gè)腳本文件讀入的還是交互輸入的,都屬于一個(gè)叫做__main__的模塊,所以也存在于自己的全局名字空間之中。(內(nèi)置名字實(shí)際上也存在于一個(gè)模塊中,這個(gè)模塊叫做__builtin__ )。

函數(shù)的局部名字空間當(dāng)函數(shù)被調(diào)用時(shí)創(chuàng)建,當(dāng)函數(shù)返回或者產(chǎn)生了一個(gè)不能在函數(shù)內(nèi)部處理的例外時(shí)被刪除。(實(shí)際上,說(shuō)是忘記了這個(gè)名字空間更符合實(shí)際發(fā)生的情況。)當(dāng)然,遞歸調(diào)用在每次遞歸中有自己的局部名字空間。

一個(gè)作用域是Python程序中的一個(gè)文本區(qū)域,其中某個(gè)名字空間可以直接訪問(wèn)?!爸苯釉L問(wèn)” 這里指的是使用不加修飾的名字就直接找到名字空間中的對(duì)象。

雖然作用域是靜態(tài)定義的,在使用時(shí)作用域是動(dòng)態(tài)的。在任何運(yùn)行時(shí)刻,總是恰好有三個(gè)作用域在使用中(即恰好有三個(gè)名字空間是直接可訪問(wèn)的):最內(nèi)層的作用域,最先被搜索,包含局部名字;中層的作用域,其次被搜索,包含當(dāng)前模塊的全局名字;最外層的作用域最后被搜索,包含內(nèi)置名字。

一般情況下,局部作用域引用當(dāng)前函數(shù)的局部名字,其中局部是源程序文本意義上來(lái)看的。在函數(shù)外部,局部作用域與全局作用域使用相同的名字空間:模塊的名字空間。類(lèi)定義在局部作用域中又增加了另一個(gè)名字空間。

一定要注意作用域是按照源程序中的文本位置確定的:模塊中定義的函數(shù)的全局作用域是模塊的名字空間,不管這個(gè)函數(shù)是從哪里調(diào)用或者以什么名字調(diào)用的。另一方面,對(duì)名字的搜索卻是在程序運(yùn)行中動(dòng)態(tài)進(jìn)行的,不過(guò),Python語(yǔ)言的定義也在演變,將來(lái)可能發(fā)展到靜態(tài)名字解析,在“編譯”時(shí),所以不要依賴(lài)于動(dòng)態(tài)名字解析?。▽?shí)際上,局部名字已經(jīng)是靜態(tài)確定的了)。

Python的一個(gè)特別之處是賦值總是進(jìn)入最內(nèi)層作用域。關(guān)于刪除也是這樣:“del x”從局部作用域?qū)?yīng)的名字空間中刪除x的名字綁定(注意在Python中可以多個(gè)名字對(duì)應(yīng)一個(gè)對(duì)象,所以刪除一個(gè)名字只是刪除了這個(gè)名字與其對(duì)象間的聯(lián)系而不一定刪除這個(gè)對(duì)象。實(shí)際上,所有引入新名字的操作都使用局部作用域:特別的,import語(yǔ)句和函數(shù)定義把模塊名或函數(shù)名綁定入局部作用域。(可以使用global語(yǔ)句指明某些變量是屬于全局名字空間的)。

9.3 初識(shí)類(lèi)
 類(lèi)引入了一些新語(yǔ)法,三種新對(duì)象類(lèi)型,以及一些新的語(yǔ)義。 

9.3.1 類(lèi)定義語(yǔ)法
 類(lèi)定義的最簡(jiǎn)單形式如下: 

class 類(lèi)名:
    <語(yǔ)句-1>
    .
    .
    .
    <語(yǔ)句-N>


如同函數(shù)定義(def語(yǔ)句)一樣,類(lèi)定義必須先執(zhí)行才能生效。(甚至可以把類(lèi)定義放在if 語(yǔ)句的一個(gè)分支中或函數(shù)中)。在實(shí)際使用時(shí),類(lèi)定義中的語(yǔ)句通常是函數(shù)定義,其它語(yǔ)句也是允許的,有時(shí)是有用的――我們后面會(huì)再提到這一點(diǎn)。類(lèi)內(nèi)的函數(shù)定義通常具有一種特別形式的自變量表,專(zhuān)用于方法的調(diào)用約定――這一點(diǎn)也會(huì)在后面詳細(xì)討論。

進(jìn)入類(lèi)定義后,產(chǎn)生了一個(gè)新的名字空間,被用作局部作用域――于是,所有對(duì)局部變量的賦值進(jìn)入這個(gè)新名字空間。特別地,函數(shù)定義把函數(shù)名與新函數(shù)綁定在這個(gè)名字空間。

當(dāng)函數(shù)定義正常結(jié)束(從結(jié)尾退出)時(shí),就生成了一個(gè)類(lèi)對(duì)象。這基本上是將類(lèi)定義生成的名字空間包裹而成的一個(gè)對(duì)象;我們?cè)谙乱还?jié)會(huì)學(xué)到類(lèi)對(duì)象的更多知識(shí)。原始的局部作用域(在進(jìn)入類(lèi)定義之前起作用的那個(gè))被恢復(fù),類(lèi)對(duì)象在這里被綁定到了類(lèi)對(duì)象定義頭部所指定的名字。

9.3.2 類(lèi)對(duì)象
類(lèi)對(duì)象支持兩種操作:屬性引用和實(shí)例化。屬性引用的格式和Python中其它的屬性引用格式相同,即obj.name。有效的屬性名包括生成類(lèi)對(duì)象時(shí)的類(lèi)名字空間中所有的名字。所以,如果象下面這樣定義類(lèi):

class MyClass:
    "A simple example class"
    i = 12345
    def f(x):
        return 'hello world'


則MyClass.i和MyClass.f都是有效的屬性引用,分別返回一個(gè)整數(shù)和一個(gè)函數(shù)對(duì)象。也可以對(duì)類(lèi)屬性賦值,所以你可以對(duì)MyClass.i賦值而改變?cè)搶傩缘闹怠?

__doc__也是一個(gè)有效的屬性,它是只讀的,返回類(lèi)的文檔字符串:“A simple example class”。

類(lèi)實(shí)例化使用函數(shù)記號(hào)。只要把這個(gè)類(lèi)對(duì)象看成是一個(gè)沒(méi)有自變量的函數(shù),返回一個(gè)類(lèi)實(shí)例。例如(假設(shè)使用上面的類(lèi)):

    x = MyClass()

可以生成該類(lèi)的一個(gè)新實(shí)例并把實(shí)例對(duì)象賦給局部變量x?!?nbsp;

9.3.3 實(shí)例對(duì)象
 我們?nèi)绾问褂脤?shí)例對(duì)象呢?類(lèi)實(shí)例只懂得屬性引用這一種操作。有兩類(lèi)有效的屬性?!?nbsp;

第一類(lèi)屬性叫做數(shù)據(jù)屬性。數(shù)據(jù)屬性相當(dāng)于Smalltalk中的“實(shí)例變量”,和C++中的“數(shù)據(jù)成員”。數(shù)據(jù)成員不需要聲明,也不需要在類(lèi)定義中已經(jīng)存在,象局部變量一樣,只要一賦值它就產(chǎn)生了。例如,如果x是上面的MyClass類(lèi)的一個(gè)實(shí)例,則下面的例子將顯示值16而不會(huì)留下任何痕跡:

x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print x.counter
del x.counter


類(lèi)實(shí)例能理解的第二類(lèi)屬性引用是方法。方法是“屬于”一個(gè)對(duì)象的函數(shù)。(在Python中,方法并不是只用于類(lèi)實(shí)例的:其它對(duì)象類(lèi)型也可以有方法,例如,列表對(duì)象也有append、insert 、remove、sort等方法。不過(guò),在這里除非特別說(shuō)明我們用方法來(lái)特指類(lèi)實(shí)例對(duì)象的方法)。

類(lèi)對(duì)象的有效方法名依賴(lài)于它的類(lèi)。按照定義,類(lèi)的所有類(lèi)型為函數(shù)對(duì)象屬性定義了其實(shí)例的對(duì)應(yīng)方法。所以在我們的例子y,x.f是一個(gè)有效的方法引用,因?yàn)镸yClass是一個(gè)函數(shù);x.i 不是方法引用,因?yàn)镸yClass.i不是。但是x.f和MyClass.f不是同一個(gè)東西――x.f是一個(gè)方法對(duì)象而不是一個(gè)函數(shù)對(duì)象。

9.3.4 方法對(duì)象
 方法一般是直接調(diào)用的,例如: 

    x.f()

在我們的例子中,這將返回字符串‘hello world'。然而,也可以不直接調(diào)用方法:x.f 是一個(gè)方法對(duì)象,可以把它保存起來(lái)再調(diào)用。例如:

xf = x.f
while 1:
    print xf()


會(huì)不停地顯示“hello world”?!?nbsp;

調(diào)用方法時(shí)到底發(fā)生了什么呢?你可能已經(jīng)注意到x.f()調(diào)用沒(méi)有自變量,而函數(shù)f在調(diào)用時(shí)有一個(gè)自變量。那個(gè)自變量是怎么回事?Python如果調(diào)用一個(gè)需要自變量的函數(shù)時(shí)忽略自變量肯定會(huì)產(chǎn)生例外錯(cuò)誤――即使那個(gè)自變量不需要用到……

實(shí)際上,你可以猜出答案:方法與函數(shù)的區(qū)別在于對(duì)象作為方法的第一個(gè)自變量自動(dòng)傳遞給方法。在我們的例子中,調(diào)用x.f()等價(jià)于調(diào)用MyClass.f(x)。一般地,用n個(gè)自變量的去調(diào)用方法等價(jià)于把方法所屬對(duì)象插入到第一個(gè)自變量前面以后調(diào)用對(duì)應(yīng)函數(shù)。

如果你還不理解方法是如何工作的,看一看方法的實(shí)現(xiàn)可能會(huì)有所幫助。在引用非數(shù)據(jù)屬性的實(shí)例屬性時(shí),將搜索它的類(lèi)。如果該屬性名是一個(gè)有效的函數(shù)對(duì)象,就生成一個(gè)方法對(duì)象,把實(shí)例對(duì)象(的指針)和函數(shù)對(duì)象包裝到一起:這就是方法對(duì)象。當(dāng)方法對(duì)象用一個(gè)自變量表調(diào)用時(shí),它再被打開(kāi)包裝,由實(shí)例對(duì)象和原自變量表組合起來(lái)形成新自變量表,用這個(gè)新自變量表調(diào)用函數(shù)。

9.4 一些說(shuō)明
在名字相同時(shí)數(shù)據(jù)屬性會(huì)覆蓋方法屬性;為了避免偶然的名字沖突,這在大型程序中會(huì)造成難以查找的錯(cuò)誤,最好按某種命名慣例來(lái)區(qū)分方法名和數(shù)據(jù)名,例如,所有方法名用大寫(xiě)字母開(kāi)頭,所有數(shù)據(jù)屬性名前用一個(gè)唯一的字符串開(kāi)頭(或者只是一個(gè)下劃線),或方法名用動(dòng)詞而數(shù)據(jù)名用名詞。

數(shù)據(jù)屬性可以被方法引用也可以被普通用戶(hù)(“客戶(hù)”)引用。換句話說(shuō),類(lèi)不能用來(lái)構(gòu)造抽象數(shù)據(jù)類(lèi)型。實(shí)際上,Python中沒(méi)有任何辦法可以強(qiáng)制進(jìn)行數(shù)據(jù)隱藏——這些都是基于慣例。(另一方面,Python的實(shí)現(xiàn)是用C寫(xiě)的,它可以完全隱藏實(shí)現(xiàn)細(xì)節(jié),必要時(shí)可以控制對(duì)象存??;用C寫(xiě)的Python擴(kuò)展模塊也有同樣特性)。

客戶(hù)要自己小心使用數(shù)據(jù)屬性——客戶(hù)可能會(huì)因?yàn)殡S意更改類(lèi)對(duì)象的數(shù)據(jù)屬性而破壞由類(lèi)方法維護(hù)的類(lèi)數(shù)據(jù)的一致性。注意客戶(hù)只要注意避免名字沖突可以任意為實(shí)例對(duì)象增加新數(shù)據(jù)屬性而不需影響到方法的有效性——這里,有效的命名慣例可以省去許多麻煩。

從方法內(nèi)要訪問(wèn)本對(duì)象的數(shù)據(jù)屬性(或其它方法)沒(méi)有一個(gè)簡(jiǎn)寫(xiě)的辦法。我認(rèn)為這事實(shí)上增加了程序的可讀性:在方法定義中不會(huì)混淆局部變量和實(shí)例變量。

習(xí)慣上,方法的第一自變量叫做self。這只不過(guò)是一個(gè)習(xí)慣用法:名字self在Python中沒(méi)有任何特殊意義。但是,因?yàn)橛脩?hù)都使用此慣例,所以違背此慣例可能使其它Python程序員不容易讀你的程序,可以想象某些類(lèi)瀏覽程序會(huì)依賴(lài)于此慣例)。

作為類(lèi)屬性的任何函數(shù)對(duì)象都為該類(lèi)的實(shí)例定義一個(gè)方法。函數(shù)的定義不一定必須在類(lèi)定義內(nèi)部:只要在類(lèi)內(nèi)把一個(gè)函數(shù)對(duì)象賦給一個(gè)局部變量就可以了。例如:

# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)
 
class C:
    f = f1
    def g(self):
        return 'hello world'
    h = g


現(xiàn)在f、g和h都是類(lèi)C的屬性且指向函數(shù)對(duì)象,所以它們都是C的實(shí)例的方法——其中h與g 完全等價(jià)。注意我們應(yīng)該避免這種用法以免誤導(dǎo)讀者。

 方法可以用代表所屬對(duì)象的self自變量來(lái)引用本類(lèi)其它的方法,如: 

class Bag:
    def empty(self):
        self.data = []
    def add(self, x):
        self.data.append(x)
    def addtwice(self, x):
        self.add(x)
        self.add(x)


實(shí)例化操作(“調(diào)用”一個(gè)類(lèi)對(duì)象)生成一個(gè)空對(duì)象。許多類(lèi)要求生成具有已知初識(shí)狀態(tài)的類(lèi)。為此,類(lèi)可以定義一個(gè)特殊的叫做__init__()的方法,如:

    def __init__(self):
        self.empty()


一個(gè)類(lèi)定義了__init__()方法以后,類(lèi)實(shí)例化時(shí)就會(huì)自動(dòng)為新生成的類(lèi)實(shí)例調(diào)用調(diào)用__init__() 方法。所以在Bag例子中,可以用如下程序生成新的初始化的實(shí)例:

    x = Bag()

當(dāng)然,__init__()方法可以有自變量,這樣可以實(shí)現(xiàn)更大的靈活性。在這樣的情況下,類(lèi)實(shí)例化時(shí)指定的自變量被傳遞給__init__()方法。例如:

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
... 
>>> x = Complex(3.0,-4.5)
>>> x.r, x.i
(3.0, -4.5)


方法可以和普通函數(shù)一樣地引用全局名字。方法的全局作用域是包含類(lèi)定義的模塊。(注意類(lèi)本身并不被用作全局作用域?。╇m然我們很少需要在方法中使用全局?jǐn)?shù)據(jù),全局作用域還是有許多合法的用途:例如,導(dǎo)入全局作用域的函數(shù)和模塊可以被方法使用,在同一模塊中定義的函數(shù)和方法也可以被方法使用。包含此方法的類(lèi)一般也在此全局作用域中定義,下一節(jié)我們會(huì)看到一個(gè)方法為什么需要引用自己的類(lèi)!

9.5 繼承
當(dāng)然,一個(gè)語(yǔ)言如果不支持繼承就談不到“類(lèi)”。導(dǎo)出類(lèi)的定義方法如下: 

class 導(dǎo)出類(lèi)名(基類(lèi)名):
    <語(yǔ)句-1>
    .
    .
    .
    <語(yǔ)句-N>


其中“基類(lèi)名”必須在包含導(dǎo)出類(lèi)定義的作用域中有定義。除了給出基類(lèi)名外,還可以給出一個(gè)表達(dá)式,在基類(lèi)定義于其它模塊中時(shí)這是有用的,如:

class 導(dǎo)出類(lèi)名 (模塊名.基類(lèi)名):

導(dǎo)出類(lèi)定義的運(yùn)行和基類(lèi)運(yùn)行的方法是一樣的。生成類(lèi)對(duì)象是,基類(lèi)被記憶。這用于解決屬性引用:如果類(lèi)中未找到要求的屬性就到基類(lèi)中去查找。如果基類(lèi)還有基類(lèi)的話這個(gè)規(guī)則遞歸地應(yīng)用到更高的類(lèi)。

導(dǎo)出類(lèi)在實(shí)例化時(shí)沒(méi)有任何特殊規(guī)則?!皩?dǎo)出類(lèi)名()”產(chǎn)生該類(lèi)的一個(gè)新實(shí)例。方法引用這樣解決:搜索相應(yīng)類(lèi)屬性,如果必要的話逐級(jí)向基類(lèi)查找,如果找到了一個(gè)函數(shù)對(duì)象就是有效的方法引用。

導(dǎo)出類(lèi)可以重寫(xiě)基類(lèi)的方法。因?yàn)榉椒ㄔ谡{(diào)用同一對(duì)象的其它方法時(shí)并無(wú)任何特殊權(quán)限,如果基類(lèi)中某一方法調(diào)用同一基類(lèi)的另一方法,在導(dǎo)出類(lèi)中該方法調(diào)用的就可能是已經(jīng)被導(dǎo)出類(lèi)重寫(xiě)后的方法了。(對(duì)C++程序員而言:Python中所有方法都是“虛擬函數(shù)”)。

導(dǎo)出類(lèi)中重寫(xiě)的方法可能是需要擴(kuò)充基類(lèi)的同名方法而不是完全代替原來(lái)的方法。導(dǎo)出類(lèi)調(diào)用基類(lèi)同名方法很簡(jiǎn)單:“基類(lèi)名.方法名(self, 自變量表)”。對(duì)類(lèi)用戶(hù)這種做法偶爾也是有用的。(注意只有基類(lèi)在同一全局作用域定義或?qū)霑r(shí)才能這樣用)。

8.5.1 多重繼承
Python也支持有限的多重繼承。有多個(gè)基類(lèi)的類(lèi)定義格式如下: 

class 導(dǎo)出類(lèi)名 (基類(lèi)1, 基類(lèi)2, 基類(lèi)3):
    <語(yǔ)句-1>
    .
    .
    .
    <語(yǔ)句-N>


關(guān)于多重繼承只需要解釋如何解決類(lèi)屬性引用。類(lèi)屬性引用是深度優(yōu)先,從左向右進(jìn)行的。所以,如果在導(dǎo)出類(lèi)定義中未找到某個(gè)屬性,就先在基類(lèi)1中查找,然后(遞歸地)在基類(lèi)1 的基類(lèi)中查找,如果都沒(méi)有找到,就在基類(lèi)2中查找,如此進(jìn)行下去。

(對(duì)某些人來(lái)說(shuō)寬度優(yōu)先——先在基類(lèi)2和基類(lèi)3中查找再到基類(lèi)1的基類(lèi)中查找——看起來(lái)更自然。然而,這需要你在確定基類(lèi)1與基類(lèi)2的屬性沖突時(shí)明確知道這個(gè)屬性是在基類(lèi)1本身定義還是在其基類(lèi)中定義。深度優(yōu)先規(guī)則不區(qū)分基類(lèi)1的一個(gè)屬性到底是直接定義的還是繼承來(lái)的)。

很顯然,如果不加約束地使用多重繼承會(huì)造成程序維護(hù)的惡夢(mèng),因?yàn)镻ython避免名字沖突只靠習(xí)慣約定。多重繼承的一個(gè)眾所周知的問(wèn)題是當(dāng)導(dǎo)出類(lèi)有兩個(gè)基類(lèi)恰好從同一個(gè)基類(lèi)導(dǎo)出的。盡管很容易想到這種情況的后果(實(shí)例只有一份“實(shí)例變量”或數(shù)據(jù)屬性被共同的基類(lèi)使用),但是這種做法有什么用處卻是不清楚的。

9.6 私有變量
Python對(duì)私有類(lèi)成員有部分支持。任何象__spam這樣形式的標(biāo)識(shí)符(至少有兩個(gè)前導(dǎo)下劃線,至多有一個(gè)結(jié)尾下劃線)目前被替換成_classname__spam,其中classname是所屬類(lèi)名去掉前導(dǎo)下劃線的結(jié)果。這種攪亂不管標(biāo)識(shí)符的語(yǔ)法位置,所以可以用來(lái)定義類(lèi)私有的實(shí)例、變量、方法,以及全局變量,甚至于保存對(duì)于此類(lèi)是私有的其它類(lèi)的實(shí)例。如果攪亂的名字超過(guò)255個(gè)字符可能會(huì)發(fā)生截?cái)?。在?lèi)外面或類(lèi)名只有下劃線時(shí)不進(jìn)行攪亂。

名字?jǐn)噥y的目的是給類(lèi)一種定義“私有”實(shí)例變量和方法的簡(jiǎn)單方法,不需擔(dān)心它的其它類(lèi)會(huì)定義同名變量,也不怕類(lèi)外的代碼弄亂實(shí)例的變量。注意攪亂規(guī)則主要是為了避免偶然的錯(cuò)誤,如果你一定想做的話仍然可以訪問(wèn)或修改私有變量。這甚至是有用的,比如調(diào)試程序要用到私有變量,這也是為什么這個(gè)漏洞沒(méi)有堵上的一個(gè)原因。(小錯(cuò)誤:導(dǎo)出類(lèi)和基類(lèi)取相同的名字就可以使用基類(lèi)的私有變量)。

注意傳遞給exec,eval()或evalfile()的代碼不會(huì)認(rèn)為調(diào)用它們的類(lèi)的類(lèi)名是當(dāng)前類(lèi),這與global語(yǔ)句的情況類(lèi)似,global的作用局限于一起字節(jié)編譯的代碼。同樣的限制也適用于getattr() ,setattr()和delattr(),以及直接訪問(wèn)__dict__的時(shí)候。

下面例子中的類(lèi)實(shí)現(xiàn)了自己的__getattr__和__setattr__方法,把所有屬性保存在一個(gè)私有變量中,這在Python的新舊版本中都是可行的:

class VirtualAttributes:
    __vdict = None
    __vdict_name = locals().keys()[0]

    def __init__(self):
        self.__dict__[self.__vdict_name] = {}

    def __getattr__(self, name):
        return self.__vdict[name]

    def __setattr__(self, name, value):
        self.__vdict[name] = value


9.7 補(bǔ)充
有時(shí)我們希望有一種類(lèi)似Pascal的“record”或C的“struct”的類(lèi)型,可以把幾個(gè)有名的數(shù)據(jù)項(xiàng)組合在一起。一個(gè)空類(lèi)可以很好地滿足這個(gè)需要,如:

class Employee:
    pass
 
john = Employee() # 生成一個(gè)空職員記錄
 
# 填充記錄的各個(gè)域
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000


一段需要以某種抽象數(shù)據(jù)類(lèi)型作為輸入的Python程序經(jīng)常可以接受一個(gè)類(lèi)作為輸入,該類(lèi)只是模仿了應(yīng)輸入的數(shù)據(jù)類(lèi)型的方法。例如,如果你有一個(gè)函數(shù)是用來(lái)格式化一個(gè)文件對(duì)象中的數(shù)據(jù),就可一個(gè)定義一個(gè)具有方法read()和readline()的類(lèi),該類(lèi)可以不從文件輸入而是從一個(gè)字符串緩沖區(qū)輸入,把這個(gè)類(lèi)作為自變量。

 實(shí)例方法對(duì)象也有屬性:m.im_self是方法所屬的實(shí)例,m.im_func是方法對(duì)應(yīng)的函數(shù)對(duì)象?!?nbsp;

9.7.1 例外可以是類(lèi)
用戶(hù)自定義的例外除了可以是字符串對(duì)象以外還可以是類(lèi)。這樣可以定義可擴(kuò)充的分層的類(lèi)例外結(jié)構(gòu)。

raise語(yǔ)句有兩種新的有效格式: 

raise 類(lèi), 實(shí)例
 
raise 實(shí)例


在第一種形式中,“實(shí)例”必須是“類(lèi)”的實(shí)例或“類(lèi)”的導(dǎo)出類(lèi)的實(shí)例。第二種形式是

raise instance.__class__, instance

的簡(jiǎn)寫(xiě)。except語(yǔ)句除了可以列出字符串對(duì)象外也可以列出類(lèi)。execpt子句中列出的類(lèi)如果是發(fā)生的例外類(lèi)或基類(lèi)則是匹配的(反過(guò)來(lái)不對(duì)——except中如果是導(dǎo)出類(lèi)而發(fā)生的例外屬于基類(lèi)時(shí)是不匹配的)。例如,下面的程序會(huì)顯示B、C、D:

class B:
    pass
class C(B):
    pass
class D(C):
    pass
 
for c in [B, C, D]:
    try:
        raise c()
    except D:
        print "D"
    except C:
        print "C"
    except B:
        print "B"


注意如果把except子句的次序顛倒過(guò)來(lái)的話(“except B”放在最前),程序?qū)@示B,B ,B——因?yàn)榈谝粋€(gè)匹配的except子句被引發(fā)。

當(dāng)沒(méi)有處理的例外是類(lèi)的時(shí)候,類(lèi)名顯示在錯(cuò)誤信息中,后面跟著一個(gè)冒號(hào)和一個(gè)空格,最后是實(shí)例用內(nèi)置函數(shù)str()轉(zhuǎn)換成字符串的結(jié)果。

相關(guān)文章

  • Python 中下劃線的幾種用法(_、_xx、xx_、__xx、__xx__)

    Python 中下劃線的幾種用法(_、_xx、xx_、__xx、__xx__)

    本文主要介紹了Python 中下劃線的幾種用法(_、_xx、xx_、__xx、__xx__),詳細(xì)的介紹了這幾種下劃線的用處,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • 解決Python3中二叉樹(shù)前序遍歷的迭代問(wèn)題

    解決Python3中二叉樹(shù)前序遍歷的迭代問(wèn)題

    二叉樹(shù)是分層數(shù)據(jù)結(jié)構(gòu),其中每個(gè)父節(jié)點(diǎn)最多有 2 個(gè)子節(jié)點(diǎn),在今天的文章中,我們將討論一個(gè)在大量技術(shù)編碼面試中出現(xiàn)的重要主題,對(duì)Python二叉樹(shù)遍歷相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-09-09
  • Python 提取dict轉(zhuǎn)換為xml/json/table并輸出的實(shí)現(xiàn)代碼

    Python 提取dict轉(zhuǎn)換為xml/json/table并輸出的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Python 提取dict轉(zhuǎn)換為xml/json/table并輸出的實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2016-08-08
  • python使用pypdf2實(shí)現(xiàn)pdf文檔解密

    python使用pypdf2實(shí)現(xiàn)pdf文檔解密

    利用pypdf2完成pdf的解密,這里的事例是python3環(huán)境下的,當(dāng)然python2下也可以運(yùn)行,只需要修改名稱(chēng)即可,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • Python selenium+cookie實(shí)現(xiàn)免密登陸的示例代碼

    Python selenium+cookie實(shí)現(xiàn)免密登陸的示例代碼

    本文主要介紹了Python selenium+cookie實(shí)現(xiàn)免密登陸的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • python異常處理和日志處理方式

    python異常處理和日志處理方式

    今天小編就為大家分享一篇python異常處理和日志處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-12-12
  • Python及Pycharm安裝方法圖文教程

    Python及Pycharm安裝方法圖文教程

    這篇文章主要為大家詳細(xì)介紹了Python及Pycharm安裝方法圖文教程,文中安裝步驟介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Tensorflow實(shí)現(xiàn)多GPU并行方式

    Tensorflow實(shí)現(xiàn)多GPU并行方式

    今天小編就為大家分享一篇Tensorflow實(shí)現(xiàn)多GPU并行方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • 用python3教你任意Html主內(nèi)容提取功能

    用python3教你任意Html主內(nèi)容提取功能

    這篇文章主要介紹了用python3教你任意Html主內(nèi)容提取功能,主要使用到了requests、lxml、json等模塊,文中逐一對(duì)這幾個(gè)模塊做了介紹,需要的朋友可以參考下
    2018-11-11
  • 常用的Python代碼調(diào)試工具總結(jié)

    常用的Python代碼調(diào)試工具總結(jié)

    今天給大家?guī)?lái)的是關(guān)于Python的相關(guān)知識(shí),文章圍繞著Python代碼調(diào)試工具展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06

最新評(píng)論