溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行python中類的全面分析

發(fā)布時(shí)間:2021-11-02 17:40:46 來(lái)源:億速云 閱讀:137 作者:柒染 欄目:web開(kāi)發(fā)

本篇文章為大家展示了如何進(jìn)行python中類的全面分析,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

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

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

  • 類:對(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):那些無(wú)須特定的對(duì)象實(shí)例就能夠工作的從屬于類的函數(shù)。

類概述

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

class Student(object):     pass

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

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

>>> bart = Student() >>> bart <__main__.Student object at 0x10a67a590> >>> Student <class '__main__.Student'>

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

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

>>> bart.name = 'Bart Simpson' >>> bart.name 'Bart Simpson'

由于類可以起到模板的作用,因此,可以在創(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ù)相比,在類中定義的對(duì)象函數(shù)(還有靜態(tài)方法,類方法)只有一點(diǎn)不同,就是***個(gè)參數(shù)永遠(yuǎn)是實(shí)例變量self,并且,調(diào)用時(shí)不用傳遞該參數(shù)。

新式類、舊式類

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

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

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

class OldClass():     pass  o = OldClass() print o.__class__   # __main__.OldClass print type(o)       # <type 'instance'>   class newClass(object):     pass  n = newClass() print n.__class__   # <class '__main__.newClass'> 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)用離不開(kāi)實(shí)例,我們必須給函數(shù)傳遞一個(gè)實(shí)例。假設(shè)對(duì)象a具有實(shí)例方法  foo(self, *args, **kwargs),那么調(diào)用的時(shí)候可以用 a.foo(*args, **kwargs),或者 A.foo(a, *args,  **kwargs),在解釋器看來(lái)它們是完全一樣的。

  • 類方法每次定義的時(shí)候需要指定類(該方法的***個(gè)參數(shù),名字約定成俗為cls),調(diào)用時(shí)和實(shí)例方法類似需要指定一個(gè)類。

  • 靜態(tài)方法其實(shí)和普通的方法一樣,只不過(guò)在調(diào)用的時(shí)候需要使用類或者實(shí)例。之所以需要靜態(tài)方法,是因?yàn)橛袝r(shí)候需要將一組邏輯上相關(guān)的函數(shù)放在一個(gè)類里面,便于組織代碼結(jié)構(gòu)。一般如果一個(gè)方法不需要用到self,那么它就適合用作靜態(tài)方法。

具體的例子如下:

def foo(x):     print "executing foo(%s)"%(x)   class A(object):     def foo(self):         print "executing foo(%s)" % self      @classmethod     def class_foo(cls):         print "executing class_foo(%s)" % cls      @staticmethod     def static_foo():         print "executing static_foo()"  a = A() print a.foo print A.foo  print a.class_foo print A.class_foo  print A.static_foo print a.static_foo print foo  # <bound method A.foo of <__main__.A object at 0x10d5f90d0>> # <unbound method A.foo> # <bound method type.class_foo of <class '__main__.A'>> # <bound method type.class_foo of <class '__main__.A'>> # <function static_foo at 0x10d5f32a8> # <function static_foo at 0x10d5f32a8> # <function foo at 0x10d5f1ed8>

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

  • 未綁定的方法:通過(guò)類來(lái)引用實(shí)例方法返回一個(gè)未綁定方法對(duì)象。要調(diào)用它,你必須顯示地提供一個(gè)實(shí)例作為***個(gè)參數(shù),比如 A.foo。

  • 綁定的方法:通過(guò)實(shí)例訪問(wèn)方法返回一個(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__":

class Student(object):     count = 0     books = []     def __init__(self, name, age):         self.name = name         self.age = age

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

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

Student.books.extend(["python", "javascript"])   print "Student book list: %s" %Student.books     # class can add class attribute after class definition Student.hobbies = ["reading", "jogging", "swimming"] print "Student hobby list: %s" %Student.hobbies     print dir(Student)  # class instance attribute wilber = Student("Wilber", 28)  print "%s is %d years old" %(wilber.name, wilber.age)    # class instance can add new attribute  # "gender" is the instance attribute only belongs to wilber wilber.gender = "male" print "%s is %s" %(wilber.name, wilber.gender)  # class instance can access class attribute     wilber.books.append("C#") print wilber.books

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

  • 類數(shù)據(jù)屬性屬于類本身,可以通過(guò)類名進(jìn)行訪問(wèn)/修改;

  • 類數(shù)據(jù)屬性也可以被類的所有實(shí)例訪問(wèn)/修改;

  • 在類定義之后,可以通過(guò)類名動(dòng)態(tài)添加類數(shù)據(jù)屬性,新增的類屬性也被類和所有實(shí)例共有;

  • 實(shí)例數(shù)據(jù)屬性只能通過(guò)實(shí)例訪問(wèn);

  • 在實(shí)例生成后,還可以動(dòng)態(tài)添加實(shí)例數(shù)據(jù)屬性,但是這些實(shí)例數(shù)據(jù)屬性只屬于該實(shí)例;

再看下面的程序

class Person:     name="aaa"  p1=Person() p2=Person() p1.name="bbb" print p1.name  # bbb print p2.name  # aaa print Person.name  # aaa

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

class Person:     name=[]  p1=Person() p2=Person() p1.name.append(1) print p1.name  # [1] print p2.name  # [1] print Person.name  # [1]

特殊的類屬性

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

如何進(jìn)行python中類的全面分析

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

如何進(jìn)行python中類的全面分析

類的繼承

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

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

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

  1. 調(diào)用父類的未綁定的構(gòu)造方法。在調(diào)用一個(gè)實(shí)例的方法時(shí),該方法的self參數(shù)會(huì)被自動(dòng)綁定到實(shí)例上(稱為綁定方法)。但如果直接調(diào)用類的方法(比如A.init),那么就沒(méi)有實(shí)例會(huì)被綁定。這樣就可以自由的提供需要的self參數(shù),這種方法稱為未綁定(unbound)方法。大多數(shù)情況下是可以正常工作的,但是多重繼承的時(shí)候可能會(huì)重復(fù)調(diào)用父類。

  2. 通過(guò) super(cls, inst).method() 調(diào)用 MRO中下一個(gè)類的函數(shù),這里有一個(gè)非常不錯(cuò)的解釋,看完后對(duì) super  應(yīng)該就熟悉了。

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

class Base(object):     def __init__(self):         print("Base.__init__")  class Derived(Base):     def __init__(self):         Base.__init__(self)         print("Derived.__init__")

supper 調(diào)用如下:

class Base(object):     def __init__(self):         print "Base.__init__"   class Derived(Base):     def __init__(self):         super(Derived, self).__init__()         print "Derived.__init__"   class Derived_2(Derived):     def __init__(self):         super(Derived_2, self).__init__()         print "Derived_2.__init__"

繼承機(jī)制 MRO

MRO  主要用于在多繼承時(shí)判斷調(diào)用的屬性來(lái)自于哪個(gè)類。Python2.2以前的類為經(jīng)典類,它是一種沒(méi)有繼承的類,實(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順序。

如何進(jìn)行python中類的全面分析

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

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

第二種,棱形繼承模式,存在公共父類(D)的多繼承,這種情況下DFS必定經(jīng)過(guò)公共父類(D)。如果這個(gè)公共父類(D)有一些初始化屬性或者方法,但是子類(C)又重寫(xiě)了這些屬性或者方法,那么按照DFS順序必定是會(huì)先找到D的屬性或方法,那么C的屬性或者方法將永遠(yuǎn)訪問(wèn)不到,導(dǎo)致C只能繼承無(wú)法重寫(xiě)(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)順序:從左到右。

如何進(jìn)行python中類的全面分析

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

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

第二種,棱形繼承模式,BFS的查找順序解決了DFS順序中的只能繼承無(wú)法重寫(xiě)的問(wèn)題。

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

如何進(jìn)行python中類的全面分析

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

多態(tài)

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

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

特殊的類方法

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

如何進(jìn)行python中類的全面分析

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

當(dāng)我們調(diào)用 x = SomeClass() 的時(shí)候,***個(gè)被調(diào)用的函數(shù)是 __new__ ,這個(gè)方法創(chuàng)建實(shí)例。接下來(lái)可以用 __init__  來(lái)指明一個(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, &lsquo;foo&rsquo;)  ,init 函數(shù)就會(huì)接到參數(shù) 10 和 &lsquo;foo&rsquo;) 。

  • __del__(self):new和init是對(duì)象的構(gòu)造器, del則是對(duì)象的銷毀器。它并非實(shí)現(xiàn)了語(yǔ)句 del x  (因此該語(yǔ)句不等同于x.__del__()),而是定義當(dāng)對(duì)象被回收時(shí)的行為。

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

class A:     def __new__(cls):         print "A.__new__ is called"  # -> this is never called  A()

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

class A(object):  # -> don't forget the object specified as base     def __new__(cls):         print "A.__new__ called"         return super(A, cls).__new__(cls)      def __init__(self):         print "A.__init__ called"  A() # A.__new__ called # A.__init__ called

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

class Sample(object):     def __str__(self):         return "SAMPLE"  class A(object):     def __new__(cls):         print "A.__new__ called"         return super(A, cls).__new__(Sample)         # return Sample()      def __init__(self):         print "A.__init__ called"  # -> is actually never called  a = A() # A.__new__ called

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

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

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

操作符

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

if instance.equals(other_instance):     # do something

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

if instance == other_instance:     # 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ù)值操作符

就像可以使用比較操作符來(lái)比較類的實(shí)例,也可以定義數(shù)值操作符的行為??梢苑殖晌孱悾阂辉僮鞣?,常見(jiàn)算數(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)算符方法和它們的常見(jiàn)版本做的工作相同,只不過(guò)是處理交換兩個(gè)操作數(shù)之后的情況。類型轉(zhuǎn)換操作符,主要用于實(shí)現(xiàn)類似 float()  這樣的內(nèi)建類型轉(zhuǎn)換函數(shù)的操作。

類的表示

使用字符串來(lái)表示類是一個(gè)相當(dāng)有用的特性。在Python中有一些內(nèi)建方法可以返回類的表示,相對(duì)應(yīng)的,也有一系列特殊方法可以用來(lái)自定義在使用這些內(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__方法可以用來(lái)管理一個(gè)自定義類中的屬性訪問(wèn)。其中:

  • getattr方法將攔截所有未定義的屬性獲取(當(dāng)要訪問(wèn)的屬性已經(jīng)定義時(shí),該方法不會(huì)被調(diào)用,至于定義不定義,是由Python能否查找到該屬性來(lái)決定的);

  • 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)的),也就是說(shuō),可以往一個(gè)類或類實(shí)例中添加或刪除一個(gè)屬性。

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

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

def  __setattr__(self, name, value):     # do-something     object.__setattr__(self, name, value)     # 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)萜鳎阈枰x__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語(yǔ)句包裹起來(lái)的代碼塊之前會(huì)調(diào)用上下文管理器的 enter 方法,執(zhí)行完語(yǔ)句體之后會(huì)執(zhí)行 exit 方法。

with 語(yǔ)句的語(yǔ)法格式如下:

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

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

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

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

上述內(nèi)容就是如何進(jìn)行python中類的全面分析,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI