乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      python中類的全面分析

       雪柳花明 2017-07-17

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

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

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

      對象屬性

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

      方法

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

      • 對于實(shí)例方法,在類里每次定義實(shí)例方法的時(shí)候都需要指定實(shí)例(該方法的第一個(gè)參數(shù),名字約定成俗為self)。這是因?yàn)閷?shí)例方法的調(diào)用離不開實(shí)例,我們必須給函數(shù)傳遞一個(gè)實(shí)例。假設(shè)對象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è)未綁定方法對象。要調(diào)用它,你必須顯示地提供一個(gè)實(shí)例作為第一個(gè)參數(shù),比如 A.foo。
      • 綁定的方法:通過實(shí)例訪問方法返回一個(gè)綁定的方法對象。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í)例屬性。

      首先看下面代碼,展示了對類數(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í)例有哪些屬性。對于類數(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]  

      特殊的類屬性

      對于所有的類,都有一組特殊的屬性:

      通過這些屬性,可以得到 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ò)的解釋,看完后對 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ù)渑判蛩惴?,具體實(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ì)算列表、元組等對象中的數(shù)據(jù)個(gè)數(shù),這里在運(yùn)行時(shí)通過參數(shù)類型確定其具體的計(jì)算過程,正是多態(tài)的一種體現(xiàn)。

      特殊的類方法

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

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

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

      • __new__(cls,[...]) 是對象實(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是對象的構(gòu)造器, del則是對象的銷毀器。它并非實(shí)現(xiàn)了語句 del x (因此該語句不等同于x.__del__()),而是定義當(dāng)對象被回收時(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()  

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

      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í)例對象,接著用這個(gè)對象來調(diào)用了init。下面的例子中,并沒有返回一個(gè)合適的對象,所以并沒有調(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è)基類,基類的相對順序保持不變。

      操作符

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

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

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

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

      Python 有許多特殊的函數(shù)對應(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)建方法可以返回類的表示,相對應(yīng)的,也有一系列特殊方法可以用來自定義在使用這些內(nèi)建函數(shù)時(shí)類的行為。

      • __str__(self) 定義對類的實(shí)例調(diào)用 str() 時(shí)的行為。
      • __repr__(self) 定義對類的實(shí)例調(diào)用 repr() 時(shí)的行為。 str() 和 repr() 最主要的差別在于“目標(biāo)用戶”,repr() 的作用是產(chǎn)生機(jī)器可讀的輸出(大部分情況下,其輸出可以作為有效的Python代碼),而 str() 則產(chǎn)生人類可讀的輸出。
      • __dir__(self) 定義對類的實(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方法對所有的屬性進(jìn)行攔截,所以,在重載它們時(shí),不能再像往常的編碼,要注意避免遞歸調(diào)用(如果出現(xiàn)遞歸,則會(huì)引起死循環(huán));然而對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ā)對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__ 。如果你想讓你的對象可以迭代,你需要定義 __iter__ ,這個(gè)方法返回一個(gè)迭代器。迭代器必須遵守迭代器協(xié)議,需要定義 __iter__ (返回它自己)和 next 方法。

      上下文管理

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

      • enter: 進(jìn)入上下文管理器的運(yùn)行時(shí)上下文。如果指定了 as 子句的話,返回值賦值給 as 子句中的 target。
      • exit: 退出與上下文管理器相關(guān)的運(yùn)行時(shí)上下文。返回一個(gè)布爾值表示是否對發(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 對一些內(nèi)建對象進(jìn)行改進(jìn),加入了對上下文管理器的支持,可以用于 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)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多