日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

python中類的全面分析

 雪柳花明 2017-07-17

面向?qū)ο笾匾母拍罹褪穷?Class)和實(shí)例(Instance),類是抽象的模板,而實(shí)例是根據(jù)類創(chuàng)建出來的一個(gè)個(gè)具體的“對(duì)象”,每個(gè)對(duì)象都擁有相同的方法,但各自的數(shù)據(jù)可能不同。

先回顧下 OOP 的常用術(shù)語:

  • 類:對(duì)具有相同數(shù)據(jù)和方法的一組對(duì)象的描述或定義。
  • 對(duì)象:對(duì)象是一個(gè)類的實(shí)例。
  • 實(shí)例(instance):一個(gè)對(duì)象的實(shí)例化實(shí)現(xiàn)。
  • 實(shí)例屬性(instance attribute):一個(gè)對(duì)象就是一組屬性的集合。
  • 實(shí)例方法(instance method):所有存取或者更新對(duì)象某個(gè)實(shí)例一條或者多條屬性的函數(shù)的集合。
  • 類屬性(classattribute):屬于一個(gè)類中所有對(duì)象的屬性,不會(huì)只在某個(gè)實(shí)例上發(fā)生變化
  • 類方法(classmethod):那些無須特定的對(duì)象實(shí)例就能夠工作的從屬于類的函數(shù)。

類概述

python中,定義類是通過class關(guān)鍵字:

  1. class Student(object): 
  2.     pass  

class后面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個(gè)類繼承下來的。通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會(huì)繼承的類。

定義好了Student類,就可以根據(jù)Student類創(chuàng)建出Student的實(shí)例,創(chuàng)建實(shí)例是通過類名+()實(shí)現(xiàn)的:

  1. >>> bart = Student() 
  2. >>> bart 
  3. <__main__.Student object at 0x10a67a590> 
  4. >>> Student 
  5. <class '__main__.Student' 

可以看到,變量bart指向的就是一個(gè)Student的object,后面的0x10a67a590是內(nèi)存地址,每個(gè)object的地址都不一樣,而Student本身則是一個(gè)類。

可以自由地給一個(gè)實(shí)例變量綁定屬性,比如,給實(shí)例bart綁定一個(gè)name屬性:

  1. >>> bart.name = 'Bart Simpson' 
  2. >>> bart.name 
  3. 'Bart Simpson'  

由于類可以起到模板的作用,因此,可以在創(chuàng)建實(shí)例的時(shí)候,把一些我們認(rèn)為必須綁定的屬性強(qiáng)制填寫進(jìn)去。通過定義一個(gè)特殊的init方法,在創(chuàng)建實(shí)例的時(shí)候,就把name,score等屬性綁上去。

  1. class Student(object): 
  2.  
  3.     def __init__(self, name, score): 
  4.         self.name = name 
  5.         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)去:

  1. >>> bart = Student('Bart Simpson', 59) 
  2. >>> bart.name 
  3. 'Bart Simpson' 
  4. >>> bart.score 
  5. 59  

和普通的函數(shù)相比,在類中定義的對(duì)象函數(shù)(還有靜態(tài)方法,類方法)只有一點(diǎn)不同,就是第一個(gè)參數(shù)永遠(yuǎn)是實(shí)例變量self,并且,調(diào)用時(shí)不用傳遞該參數(shù)。

新式類、舊式類

python的新式類是2.2版本引進(jìn)來的,之前的類叫做經(jīng)典類或者舊類。Python 2.x 中如果一個(gè)類繼承于一個(gè)基類(可以是自定義類或者其它類)或者繼承自 object,則該類為新式類;沒有繼承的類為經(jīng)典類。Python 3.x 則全部為新式類。

新式類被賦予了很多新的特性(如:統(tǒng)一了types和classes),并改變了以往經(jīng)典類的一些內(nèi)容(如:改變了多繼承下方法的執(zhí)行順序)。

關(guān)于統(tǒng)一類(class)和類型(type),具體看下面的例子

  1. class OldClass(): 
  2.     pass 
  3.  
  4. o = OldClass() 
  5. print o.__class__   # __main__.OldClass 
  6. print type(o)       # <type 'instance'
  7.  
  8.  
  9. class newClass(object): 
  10.     pass 
  11.  
  12. n = newClass() 
  13. print n.__class__   # <class '__main__.newClass'
  14. print type(n)       # <class '__main__.newClass' 

對(duì)象屬性

Python 中對(duì)象的屬性包含對(duì)象的所有內(nèi)容:方法和數(shù)據(jù),注意方法也是對(duì)象的屬性。查找對(duì)象的屬性時(shí),首先在對(duì)象的__dict__ 里面查找,然后是對(duì)象所屬類的dict,再往后是繼承體系中父類(MRO解析)的dict,任意一個(gè)地方查找到就終止查找,并且調(diào)用 __getattribute__(也有可能是__getattr__) 方法獲得屬性值。

方法

在 Python 類中有3種方法,即靜態(tài)方法(staticmethod),類方法(classmethod)和實(shí)例方法:

  • 對(duì)于實(shí)例方法,在類里每次定義實(shí)例方法的時(shí)候都需要指定實(shí)例(該方法的第一個(gè)參數(shù),名字約定成俗為self)。這是因?yàn)閷?shí)例方法的調(diào)用離不開實(shí)例,我們必須給函數(shù)傳遞一個(gè)實(shí)例。假設(shè)對(duì)象a具有實(shí)例方法 foo(self, *args, **kwargs),那么調(diào)用的時(shí)候可以用 a.foo(*args, **kwargs),或者 A.foo(a, *args, **kwargs),在解釋器看來它們是完全一樣的。
  • 類方法每次定義的時(shí)候需要指定類(該方法的第一個(gè)參數(shù),名字約定成俗為cls),調(diào)用時(shí)和實(shí)例方法類似需要指定一個(gè)類。
  • 靜態(tài)方法其實(shí)和普通的方法一樣,只不過在調(diào)用的時(shí)候需要使用類或者實(shí)例。之所以需要靜態(tài)方法,是因?yàn)橛袝r(shí)候需要將一組邏輯上相關(guān)的函數(shù)放在一個(gè)類里面,便于組織代碼結(jié)構(gòu)。一般如果一個(gè)方法不需要用到self,那么它就適合用作靜態(tài)方法。

具體的例子如下:

  1. def foo(x): 
  2.     print "executing foo(%s)"%(x) 
  3.  
  4.  
  5. class A(object): 
  6.     def foo(self): 
  7.         print "executing foo(%s)" % self 
  8.  
  9.     @classmethod 
  10.     def class_foo(cls): 
  11.         print "executing class_foo(%s)" % cls 
  12.  
  13.     @staticmethod 
  14.     def static_foo(): 
  15.         print "executing static_foo()" 
  16.  
  17. a = A() 
  18. print a.foo 
  19. print A.foo 
  20.  
  21. print a.class_foo 
  22. print A.class_foo 
  23.  
  24. print A.static_foo 
  25. print a.static_foo 
  26. print foo 
  27.  
  28. # <bound method A.foo of <__main__.A object at 0x10d5f90d0>> 
  29. # <unbound method A.foo> 
  30. # <bound method type.class_foo of <class '__main__.A'>> 
  31. # <bound method type.class_foo of <class '__main__.A'>> 
  32. # <function static_foo at 0x10d5f32a8> 
  33. # <function static_foo at 0x10d5f32a8> 
  34. # <function foo at 0x10d5f1ed8>  

在訪問類方法的時(shí)候有兩種方法,分別叫做 未綁定的方法(unbound method) 和 綁定的方法(bound method):

  • 未綁定的方法:通過類來引用實(shí)例方法返回一個(gè)未綁定方法對(duì)象。要調(diào)用它,你必須顯示地提供一個(gè)實(shí)例作為第一個(gè)參數(shù),比如 A.foo。
  • 綁定的方法:通過實(shí)例訪問方法返回一個(gè)綁定的方法對(duì)象。Python自動(dòng)地給方法綁定一個(gè)實(shí)例,所以調(diào)用它時(shí)不用再傳一個(gè)實(shí)例參數(shù),比如 a.foo。

數(shù)據(jù)屬性

下面創(chuàng)建了一個(gè)Student的類,并且實(shí)現(xiàn)了這個(gè)類的初始化函數(shù)"__init__":

  1. class Student(object): 
  2.     count = 0 
  3.     books = [] 
  4.     def __init__(self, name, age): 
  5.         self.name = name 
  6.         self.age = age  

在上面的Student類中,count, books, name 和 age 都被稱為類的數(shù)據(jù)屬性,但是它們又分為類數(shù)據(jù)屬性和實(shí)例數(shù)據(jù)屬性。直接定義在類體中的屬性叫類屬性,而在類的方法中定義的屬性叫實(shí)例屬性。

首先看下面代碼,展示了對(duì)類數(shù)據(jù)屬性和實(shí)例數(shù)據(jù)屬性的訪問:

  1. Student.books.extend(["python""javascript"])   
  2. print "Student book list: %s" %Student.books     
  3. # class can add class attribute after class definition 
  4. Student.hobbies = ["reading""jogging""swimming"
  5. print "Student hobby list: %s" %Student.hobbies     
  6. print dir(Student) 
  7.  
  8. # class instance attribute 
  9. wilber = Student("Wilber", 28)  
  10. print "%s is %d years old" %(wilber.name, wilber.age)    
  11. # class instance can add new attribute  
  12. "gender" is the instance attribute only belongs to wilber 
  13. wilber.gender = "male" 
  14. print "%s is %s" %(wilber.name, wilber.gender) 
  15.  
  16. # class instance can access class attribute     
  17. wilber.books.append("C#"
  18. print wilber.books   

通過內(nèi)建函數(shù)dir(),或者訪問類的字典屬性__dict__,這兩種方式都可以查看類或者實(shí)例有哪些屬性。對(duì)于類數(shù)據(jù)屬性和實(shí)例數(shù)據(jù)屬性,可以總結(jié)為:

  • 類數(shù)據(jù)屬性屬于類本身,可以通過類名進(jìn)行訪問/修改;
  • 類數(shù)據(jù)屬性也可以被類的所有實(shí)例訪問/修改;
  • 在類定義之后,可以通過類名動(dòng)態(tài)添加類數(shù)據(jù)屬性,新增的類屬性也被類和所有實(shí)例共有;
  • 實(shí)例數(shù)據(jù)屬性只能通過實(shí)例訪問;
  • 在實(shí)例生成后,還可以動(dòng)態(tài)添加實(shí)例數(shù)據(jù)屬性,但是這些實(shí)例數(shù)據(jù)屬性只屬于該實(shí)例;

再看下面的程序

  1. class Person: 
  2.     name="aaa" 
  3.  
  4. p1=Person() 
  5. p2=Person() 
  6. p1.name="bbb" 
  7. print p1.name  # bbb 
  8. print p2.name  # aaa 
  9. print Person.name  # aaa  

上面程序中,p1.name="bbb"是實(shí)例調(diào)用了類變量,p1.name一開始是指向的類變量name="aaa",但是在實(shí)例的作用域里把類變量的引用改變了,就變成了一個(gè)實(shí)例變量。self.name不再引用Person的類變量name了。

  1. class Person: 
  2.     name=[] 
  3.  
  4. p1=Person() 
  5. p2=Person() 
  6. p1.name.append(1) 
  7. print p1.name  # [1] 
  8. print p2.name  # [1] 
  9. print Person.name  # [1]  

特殊的類屬性

對(duì)于所有的類,都有一組特殊的屬性:

通過這些屬性,可以得到 Student類的一些信息,如下:

類的繼承

Python 是面向?qū)ο笳Z言,支持類的繼承(包括單重和多重繼承),繼承的語法如下:

  1. class DerivedClass(BaseClass1, [BaseClass2...]): 
  2.     <statement-1> 
  3.     . 
  4.     <statement-N>  

子類可以覆蓋父類的方法,此時(shí)有兩種方法來調(diào)用父類中的函數(shù):

  1. 調(diào)用父類的未綁定的構(gòu)造方法。在調(diào)用一個(gè)實(shí)例的方法時(shí),該方法的self參數(shù)會(huì)被自動(dòng)綁定到實(shí)例上(稱為綁定方法)。但如果直接調(diào)用類的方法(比如A.init),那么就沒有實(shí)例會(huì)被綁定。這樣就可以自由的提供需要的self參數(shù),這種方法稱為未綁定(unbound)方法。大多數(shù)情況下是可以正常工作的,但是多重繼承的時(shí)候可能會(huì)重復(fù)調(diào)用父類。
  2. 通過 super(cls, inst).method() 調(diào)用 MRO中下一個(gè)類的函數(shù),這里有一個(gè)非常不錯(cuò)的解釋,看完后對(duì) super 應(yīng)該就熟悉了。

未綁定(unbound)方法調(diào)用如下:

  1. class Base(object): 
  2.     def __init__(self): 
  3.         print("Base.__init__"
  4.  
  5. class Derived(Base): 
  6.     def __init__(self): 
  7.         Base.__init__(self) 
  8.         print("Derived.__init__" 

supper 調(diào)用如下:

  1. class Base(object): 
  2.     def __init__(self): 
  3.         print "Base.__init__" 
  4.  
  5.  
  6. class Derived(Base): 
  7.     def __init__(self): 
  8.         super(Derived, self).__init__() 
  9.         print "Derived.__init__" 
  10.  
  11.  
  12. class Derived_2(Derived): 
  13.     def __init__(self): 
  14.         super(Derived_2, self).__init__() 
  15.         print "Derived_2.__init__"  

繼承機(jī)制 MRO

MRO 主要用于在多繼承時(shí)判斷調(diào)用的屬性來自于哪個(gè)類。Python2.2以前的類為經(jīng)典類,它是一種沒有繼承的類,實(shí)例類型都是type類型,如果經(jīng)典類被作為父類,子類調(diào)用父類的構(gòu)造函數(shù)時(shí)會(huì)出錯(cuò)。這時(shí)MRO的方法為DFS(深度優(yōu)先搜索),子節(jié)點(diǎn)順序:從左到右。inspect.getmro(A)可以查看經(jīng)典類的MRO順序。

兩種繼承模式在DFS下的優(yōu)缺點(diǎn):

第一種,兩個(gè)互不相關(guān)的類的多繼承,這種情況DFS順序正常,不會(huì)引起任何問題;

第二種,棱形繼承模式,存在公共父類(D)的多繼承,這種情況下DFS必定經(jīng)過公共父類(D)。如果這個(gè)公共父類(D)有一些初始化屬性或者方法,但是子類(C)又重寫了這些屬性或者方法,那么按照DFS順序必定是會(huì)先找到D的屬性或方法,那么C的屬性或者方法將永遠(yuǎn)訪問不到,導(dǎo)致C只能繼承無法重寫(override)。這也就是新式類不使用DFS的原因,因?yàn)樗麄兌加幸粋€(gè)公共的祖先object。

為了使類和內(nèi)置類型更加統(tǒng)一,Python2.2版本引入了新式類。新式類的每個(gè)類都繼承于一個(gè)基類,可以是自定義類或者其它類,默認(rèn)承于object,子類可以調(diào)用父類的構(gòu)造函數(shù)??梢杂?A.__mro__ 可以查看新式類的順序。

在 2.2 中,有兩種MRO的方法:

  1. 如果是經(jīng)典類MRO為DFS;
  2. 如果是新式類MRO為BFS(廣度優(yōu)先搜索),子節(jié)點(diǎn)順序:從左到右。

新式類兩種繼承模式在BFS下的優(yōu)缺點(diǎn):

第一種,正常繼承模式。比如B明明繼承了D的某個(gè)屬性(假設(shè)為foo),C中也實(shí)現(xiàn)了這個(gè)屬性foo,那么BFS明明先訪問B然后再去訪問C,但是A的foo屬性是c,這個(gè)問題稱為單調(diào)性問題。

第二種,棱形繼承模式,BFS的查找順序解決了DFS順序中的只能繼承無法重寫的問題。

因?yàn)镈FS 和 BFS 都存在較大的問題,所以從Python2.3開始新式類的 MRO采用了C3算法,解決了單調(diào)性問題,和只能繼承無法重寫的問題。MRO的C3算法順序如下圖:

C3 采用圖的拓?fù)渑判蛩惴ǎ唧w實(shí)現(xiàn)可以參考官網(wǎng)文檔。

多態(tài)

多態(tài)即多種形態(tài),在運(yùn)行時(shí)確定其狀態(tài),在編譯階段無法確定其類型,這就是多態(tài)。Python中的多態(tài)和Java以及C++中的多態(tài)有點(diǎn)不同,Python中的變量是動(dòng)態(tài)類型的,在定義時(shí)不用指明其類型,它會(huì)根據(jù)需要在運(yùn)行時(shí)確定變量的類型。

Python本身是一種解釋性語言,不進(jìn)行預(yù)編譯,因此它就只在運(yùn)行時(shí)確定其狀態(tài),故也有人說Python是一種多態(tài)語言。在Python中很多地方都可以體現(xiàn)多態(tài)的特性,比如內(nèi)置函數(shù)len(object),len函數(shù)不僅可以計(jì)算字符串的長度,還可以計(jì)算列表、元組等對(duì)象中的數(shù)據(jù)個(gè)數(shù),這里在運(yùn)行時(shí)通過參數(shù)類型確定其具體的計(jì)算過程,正是多態(tài)的一種體現(xiàn)。

特殊的類方法

類中經(jīng)常有一些方法用兩個(gè)下劃線包圍來命名,下圖給出一些例子。合理地使用它們可以對(duì)類添加一些“魔法”的行為。

構(gòu)造與析構(gòu)

當(dāng)我們調(diào)用 x = SomeClass() 的時(shí)候,第一個(gè)被調(diào)用的函數(shù)是 __new__ ,這個(gè)方法創(chuàng)建實(shí)例。接下來可以用 __init__ 來指明一個(gè)對(duì)象的初始化行為。當(dāng)這個(gè)對(duì)象的生命周期結(jié)束的時(shí)候, __del__ 會(huì)被調(diào)用。

  • __new__(cls,[...]) 是對(duì)象實(shí)例化時(shí)第一個(gè)調(diào)用的方法,它只取下 cls 參數(shù),并把其他參數(shù)傳給init。
  • __init__(self,[...]) 為類的初始化方法。它獲取任何傳給構(gòu)造器的參數(shù)(比如我們調(diào)用 x = SomeClass(10, ‘foo’) ,init 函數(shù)就會(huì)接到參數(shù) 10 和 ‘foo’) 。
  • __del__(self):new和init是對(duì)象的構(gòu)造器, del則是對(duì)象的銷毀器。它并非實(shí)現(xiàn)了語句 del x (因此該語句不等同于x.__del__()),而是定義當(dāng)對(duì)象被回收時(shí)的行為。

當(dāng)我們創(chuàng)建一個(gè)類的實(shí)例時(shí),首先會(huì)調(diào)用new創(chuàng)建實(shí)例,接著才會(huì)調(diào)用init來進(jìn)行初始化。不過注意在舊式類中,實(shí)例的創(chuàng)建并沒有調(diào)用new方法,如下例子:

  1. class A: 
  2.     def __new__(cls): 
  3.         print "A.__new__ is called"  # -> this is never called 
  4.  
  5. A()  

對(duì)于新式類來說,我們可以覆蓋new方法,注意該方法的第一個(gè)參數(shù)cls(其實(shí)就是當(dāng)前類類型)用來指明要?jiǎng)?chuàng)建的類型,后續(xù)參數(shù)用來傳遞給init進(jìn)行初始化。如果new返回了cls類型的對(duì)象,那么接下來調(diào)用init,否則的話不會(huì)調(diào)用init(調(diào)用該方法必須傳遞一個(gè)實(shí)例對(duì)象)。

  1. class A(object):  # -> don't forget the object specified as base 
  2.     def __new__(cls): 
  3.         print "A.__new__ called" 
  4.         return super(A, cls).__new__(cls) 
  5.  
  6.     def __init__(self): 
  7.         print "A.__init__ called" 
  8.  
  9. A() 
  10. # A.__new__ called 
  11. # A.__init__ called  

這里我們調(diào)用super()來獲取 MRO 中A的下一個(gè)類(在這里其實(shí)就是基類 object)的new方法來創(chuàng)建一個(gè)cls的實(shí)例對(duì)象,接著用這個(gè)對(duì)象來調(diào)用了init。下面的例子中,并沒有返回一個(gè)合適的對(duì)象,所以并沒有調(diào)用init:

  1. class Sample(object): 
  2.     def __str__(self): 
  3.         return "SAMPLE" 
  4.  
  5. class A(object): 
  6.     def __new__(cls): 
  7.         print "A.__new__ called" 
  8.         return super(A, cls).__new__(Sample) 
  9.         # return Sample() 
  10.  
  11.     def __init__(self): 
  12.         print "A.__init__ called"  # -> is actually never called 
  13.  
  14. a = A() 
  15. # A.__new__ called  

關(guān)于 super,這里是一個(gè)非常不錯(cuò)的解釋,簡單來說super做了下面的事情:

  1. def super(cls, inst): 
  2.     mro = inst.__class__.mro() 
  3.     return mro[mro.index(cls) + 1]  

關(guān)于 MRO,這篇文章非常棒:你真的理解Python中MRO算法嗎?,簡單來說,在新式類MRO的 C3 算法中,保證:基類永遠(yuǎn)出現(xiàn)在派生類后面,如果有多個(gè)基類,基類的相對(duì)順序保持不變。

操作符

利用特殊方法可以構(gòu)建一個(gè)擁有Python內(nèi)置類型行為的對(duì)象,這意味著可以避免使用非標(biāo)準(zhǔn)的、丑陋的方式來表達(dá)簡單的操作。在一些語言中,這樣做很常見:

  1. if instance.equals(other_instance): 
  2.     # do something  

Python中當(dāng)然也可以這么做,但是這樣做讓代碼變得冗長而混亂。不同的類庫可能對(duì)同一種比較操作采用不同的方法名稱,這讓使用者需要做很多沒有必要的工作。因此我們可以定義方法__eq__,然后就可以像下面這樣使用:

  1. if instance == other_instance: 
  2.     # do something  

Python 有許多特殊的函數(shù)對(duì)應(yīng)到常用的操作符上,比如:

  • __cmp__(self, other):定義了所有比較操作符的行為。應(yīng)該在 self < other 時(shí)返回一個(gè)負(fù)整數(shù),在 self == other 時(shí)返回0,在 self > other 時(shí)返回正整數(shù)。
  • __eq__(self, other):定義等于操作符(==)的行為。
  • __ne__(self, other):定義不等于操作符(!=)的行為(定義了 eq 的情況下也必須再定義 ne!)
  • __le__(self, other):定義小于等于操作符(<)的行為。
  • __ge__(self, other):定義大于等于操作符(>)的行為。

數(shù)值操作符

就像可以使用比較操作符來比較類的實(shí)例,也可以定義數(shù)值操作符的行為。可以分成五類:一元操作符,常見算數(shù)操作符,反射算數(shù)操作符,增強(qiáng)賦值操作符,和類型轉(zhuǎn)換操作符,下面為一些例子:

  • __pos__(self) 實(shí)現(xiàn)取正操作,例如 +some_object
  • __invert__(self) 實(shí)現(xiàn)取反操作符 ~
  • __add__(self, other) 實(shí)現(xiàn)加法操作
  • __sub__(self, other) 實(shí)現(xiàn)減法操作
  • __radd__(self, other) 實(shí)現(xiàn)反射加法操作
  • __rsub__(self, other) 實(shí)現(xiàn)反射減法操作
  • __floordiv__(self, other) 實(shí)現(xiàn)使用 // 操作符的整數(shù)除法
  • __iadd__(self, other) 實(shí)現(xiàn)加法賦值操作。
  • __isub__(self, other) 實(shí)現(xiàn)減法賦值操作。
  • __int__(self) 實(shí)現(xiàn)到int的類型轉(zhuǎn)換。
  • __long__(self) 實(shí)現(xiàn)到long的類型轉(zhuǎn)換。

反射運(yùn)算符方法和它們的常見版本做的工作相同,只不過是處理交換兩個(gè)操作數(shù)之后的情況。類型轉(zhuǎn)換操作符,主要用于實(shí)現(xiàn)類似 float() 這樣的內(nèi)建類型轉(zhuǎn)換函數(shù)的操作。

類的表示

使用字符串來表示類是一個(gè)相當(dāng)有用的特性。在Python中有一些內(nèi)建方法可以返回類的表示,相對(duì)應(yīng)的,也有一系列特殊方法可以用來自定義在使用這些內(nèi)建函數(shù)時(shí)類的行為。

  • __str__(self) 定義對(duì)類的實(shí)例調(diào)用 str() 時(shí)的行為。
  • __repr__(self) 定義對(duì)類的實(shí)例調(diào)用 repr() 時(shí)的行為。 str() 和 repr() 最主要的差別在于“目標(biāo)用戶”,repr() 的作用是產(chǎn)生機(jī)器可讀的輸出(大部分情況下,其輸出可以作為有效的Python代碼),而 str() 則產(chǎn)生人類可讀的輸出。
  • __dir__(self) 定義對(duì)類的實(shí)例調(diào)用 dir() 時(shí)的行為,這個(gè)方法應(yīng)該向調(diào)用者返回一個(gè)屬性列表。如果重定義了__getattr__ 或者使用動(dòng)態(tài)生成的屬性,以實(shí)現(xiàn)類的交互式使用,那么這個(gè)方法是必不可少的。

屬性控制

在Python中,重載__getattr__、__setattr__、__delattr__和__getattribute__方法可以用來管理一個(gè)自定義類中的屬性訪問。其中:

  • getattr方法將攔截所有未定義的屬性獲取(當(dāng)要訪問的屬性已經(jīng)定義時(shí),該方法不會(huì)被調(diào)用,至于定義不定義,是由Python能否查找到該屬性來決定的);
  • getattribute方法將攔截所有屬性的獲取(不管該屬性是否已經(jīng)定義,只要獲取它的值,該方法都會(huì)調(diào)用),由于此情況,所以,當(dāng)一個(gè)類中同時(shí)重載了getattr和getattribute方法,那么getattr永遠(yuǎn)不會(huì)被調(diào)用,另外getattribute方法僅僅存在于Python2.6的新式類和Python3的所有類中;
  • setattr方法將攔截所有的屬性賦值;
  • delattr方法將攔截所有的屬性刪除。

在Python中,一個(gè)類或類實(shí)例中的屬性是動(dòng)態(tài)的(因?yàn)镻ython是動(dòng)態(tài)的),也就是說,可以往一個(gè)類或類實(shí)例中添加或刪除一個(gè)屬性。

由于getattribute、setattr、delattr方法對(duì)所有的屬性進(jìn)行攔截,所以,在重載它們時(shí),不能再像往常的編碼,要注意避免遞歸調(diào)用(如果出現(xiàn)遞歸,則會(huì)引起死循環(huán));然而對(duì)getattr方法,則沒有這么多的限制。

在重載setattr方法時(shí),不能使用“self.name = value”格式,否則,它將會(huì)導(dǎo)致遞歸調(diào)用而陷入死循環(huán)。正確的應(yīng)該是:

  1. def  __setattr__(self, name, value): 
  2.     # do-something 
  3.     object.__setattr__(self, name, value) 
  4.     # do-something  

其中的object.__setattr__(self, name, value)一句可以換成self.__dict__[name] = value;但前提是,必須保證getattribute方法重載正確(如果重載了getattribute方法的話),否則,將在賦值時(shí)導(dǎo)致錯(cuò)誤,因?yàn)閟elf.dict將要觸發(fā)對(duì)self所有屬性中的dict屬性的獲取,這樣從而就會(huì)引發(fā)getattribute方法的調(diào)用,如果getattribute方法重載錯(cuò)誤,setattr方法自然而然也就會(huì)失敗。

自定義序列

有許多辦法可以讓 Python 類表現(xiàn)得像是內(nèi)建序列類型(字典,元組,列表,字符串等)。

在Python中實(shí)現(xiàn)自定義容器類型需要用到一些協(xié)議。首先,不可變?nèi)萜黝愋陀腥缦聟f(xié)議:想實(shí)現(xiàn)一個(gè)不可變?nèi)萜?,你需要定義__len__ 和 __getitem__。

可變?nèi)萜鞯膮f(xié)議除了上面提到的兩個(gè)方法之外,還需要定義 __setitem__ 和 __delitem__ 。如果你想讓你的對(duì)象可以迭代,你需要定義 __iter__ ,這個(gè)方法返回一個(gè)迭代器。迭代器必須遵守迭代器協(xié)議,需要定義 __iter__ (返回它自己)和 next 方法。

上下文管理

上下文管理協(xié)議(Context Management Protocol)包含方法 __enter__() 和 __exit__(),支持 該協(xié)議的對(duì)象要實(shí)現(xiàn)這兩個(gè)方法。

  • enter: 進(jìn)入上下文管理器的運(yùn)行時(shí)上下文。如果指定了 as 子句的話,返回值賦值給 as 子句中的 target。
  • exit: 退出與上下文管理器相關(guān)的運(yùn)行時(shí)上下文。返回一個(gè)布爾值表示是否對(duì)發(fā)生的異常進(jìn)行處理。

在執(zhí)行with語句包裹起來的代碼塊之前會(huì)調(diào)用上下文管理器的 enter 方法,執(zhí)行完語句體之后會(huì)執(zhí)行 exit 方法。

with 語句的語法格式如下:

  1. with context_expression [as target(s)]: 
  2.     with-body  

Python 對(duì)一些內(nèi)建對(duì)象進(jìn)行改進(jìn),加入了對(duì)上下文管理器的支持,可以用于 with 語句中,比如可以自動(dòng)關(guān)閉文件、線程鎖的自動(dòng)獲取和釋放等。如下面例子:

  1. >>> with open("etc/CS.json"as d: 
  2. ...:     print d 
  3. <open file 'etc/CS.json', mode 'r' at 0x109344540> 
  4. >>> print d 
  5. <closed file 'etc/CS.json', mode 'r' at 0x109344540> 
  6. >>> print dir(d) 
  7. ['__class__''__delattr__''__doc__''__enter__''__exit__', ...]  

通過使用 with 語句,不管在處理文件過程中是否發(fā)生異常,都能保證 with 語句執(zhí)行完畢后已經(jīng)關(guān)閉了打開的文件句柄。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多