• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 41, 文章 - 8, 評(píng)論 - 8, 引用 - 0
            數(shù)據(jù)加載中……

            python in nutshell Section 5.1

            本來用sphinx+rst輸出html,但貼上來發(fā)現(xiàn)效果不行,因此直接貼上rst的。

            Section 5.1 Classes and Instances
            +++++++++++++++++++++++++++++++++

            如果你已經(jīng)熟悉面向?qū)ο缶幊?,像在其他語言,C++或Java,這樣你可能有一個(gè)很好的理解,像類和實(shí)例:一個(gè)類,就是一個(gè)用戶定義的類型,你可以將它實(shí)例化得到實(shí)例對(duì)象,而它們就是這個(gè)類型的。Python支持這些概念,通過它的類和各個(gè)實(shí)例對(duì)象。

            5.1.1 Python Classes
            --------------------

            類就是有一些特性的python對(duì)象:

            * 你可以調(diào)用一個(gè)類,好像它就是一個(gè)函數(shù)。這個(gè)調(diào)用返回了另一個(gè)對(duì)象,這就是一個(gè)類的實(shí)例;而類也就是這個(gè)實(shí)例的類型。
            * 一個(gè)類的屬性可以任意的綁定和引用。
            * 類屬性的值可以是描述符(包括函數(shù)),或者是普通的數(shù)據(jù)對(duì)象。
            * 類屬性綁定了函數(shù),就是我們所知的類的方法。
            * 一個(gè)方法,可以有一個(gè)特殊的python定義的名字,它是以兩個(gè)下劃線起始,而又以兩個(gè)下劃線結(jié)尾。Python會(huì)隱式地調(diào)用這些特殊的方法,如果一個(gè)類提供了它們,并且當(dāng)各個(gè)操作發(fā)生在那個(gè)類的實(shí)例上。
            * 一個(gè)類可以從其他類中繼承,這就意味著它如果在本身中沒有找到一個(gè)屬性,那么它就會(huì)委托給其他類,讓它們?cè)谒鼈兊念愔兴阉鳌?/p>

            一個(gè)類的實(shí)例,是一個(gè)python對(duì)象,有任意命名了的屬性,而你可以綁定和引用。一個(gè)實(shí)例對(duì)象隱式地將在實(shí)例本身中未搜尋到的屬性委托給它的類來搜索。而這個(gè)類,如果可以,又會(huì)依次的搜索它所繼承的類。

            在python中,類是對(duì)象,而且可以像其他對(duì)象那樣使用。因此,你可以將一個(gè)類作為參數(shù),傳遞給一個(gè)函數(shù)。同樣的,一個(gè)函數(shù)可以返回一個(gè)類。一個(gè)類,就像其他的對(duì)象,可以綁定一個(gè)變量(全局或局部),一個(gè)容器中的元素,或者是一個(gè)對(duì)象的屬性。類也可以成為一個(gè)字典的鍵值。事實(shí)上,類是一個(gè)普通的對(duì)象,這也就是常說的類是一級(jí)對(duì)象。

            5.1.2 The class Statement
            -------------------------

            ``class`` 語句是最常見的方式,來創(chuàng)建一個(gè)類對(duì)象。 ``class`` 是一個(gè)單子句符合語句,遵循下列的語法:

            ::

                class classname(base-classes):

                    statement(s)

            ``classname`` 是一個(gè)標(biāo)識(shí)符。它是一個(gè)變量,可以綁定到類對(duì)象,在 ``class`` 語句完成執(zhí)行后。

            ``base-classes`` 是一系列以逗號(hào)分隔,而值都必須是類對(duì)象的表達(dá)式。這些類在不同的編程語言中有著不同的名字;這完全看你喜歡,可以是基類,超類,或者是被創(chuàng)建類的父類。而被創(chuàng)建的類,可以說是繼承,派生,擴(kuò)展,或是子類。這都完全看你所熟悉的語言是怎么稱呼它們的。這個(gè)類也可以說是一個(gè)直接子類,或者是它基類的后代。

            語法上, ``base-classes`` 是可選的:為了表示你創(chuàng)建的類是沒有基類的,你可以忽略 ``base-classes`` (而且還有它兩邊的括號(hào)也要忽略),直接將冒號(hào)放在類名字后面(在python 2.5中,你也可以使用這對(duì)空括號(hào),其意思是一樣的)。但是,一個(gè)沒有基類的類,由于向后兼容的問題,所以是一個(gè) **舊式** 的類(除非你定義了 ``__metaclass__`` 屬性)。為了創(chuàng)建 **新式** 的類,而又沒有“真正”的基類,那么這樣編寫代碼: ``class C(object):`` ;因?yàn)槊總€(gè)類型都把內(nèi)置對(duì)象歸入子類,為了區(qū)分是新式的類還是舊式的類,我們就用 ``object`` 來加以指明。如果你的類的祖先都是舊式的類,而又沒有定義 ``__metaclass__`` 屬性,那么它就是舊式的;否則,有基類的類總是新式的類(就算有些基類是新式的,有些是舊式的)。

            在各個(gè)類中的子類關(guān)系,是可傳遞的:如果 ``C1`` 是 ``C2`` 的子類,而 ``C2`` 又是 ``C3`` 的子類,那么, ``C1`` 就是 ``C3`` 的子類。內(nèi)置函數(shù) ``issubclass(C1,C2)`` 接受兩個(gè)是類對(duì)象的參數(shù):它將會(huì)返回 ``True`` 如果 ``C1`` 是 ``C2`` 的子類;否則將返回 ``False`` 。任何一個(gè)類將被認(rèn)為是自己的子類;所以, ``issubclass(C,C)`` 將會(huì)返回 ``True`` 。關(guān)于基類如何影響這個(gè)類的方式可以參考“繼承”。

            而在 ``class`` 語句后面的不為空的語句,則被稱為類體。一個(gè)類體在 ``class`` 語句被執(zhí)行時(shí)也就被作為其一部分被執(zhí)行了。直到類體結(jié)束執(zhí)行,一個(gè)新的類對(duì)象還不存在,并且 ``classname`` 標(biāo)識(shí)符也并未綁定(或重綁定)。在“如何用元類構(gòu)造類中”提供了更詳細(xì)的信息。

            最后,注意, ``class`` 語句并不是立即創(chuàng)建任何新類的實(shí)例,而是定義了一系列的屬性的集合,而這個(gè)集合又被你后來創(chuàng)建的實(shí)例所共享。

            5.1.3 The Class Body
            --------------------

            一個(gè)類的主體,是你指定類屬性的地方;這些屬性可以是描述符對(duì)象(包括函數(shù)),或者是任意類型的普通數(shù)據(jù)對(duì)象(一個(gè)類的屬性可以是另一個(gè)類,舉個(gè)例子,你可以有一個(gè)嵌套的 ``class`` 語句在另一個(gè) ``class`` 語句中。)

            5.1.3.1 Attributes of class objects
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            你可以指定一個(gè)類的屬性,通過綁定一個(gè)值給類體中的標(biāo)識(shí)符。舉個(gè)例子:

            ::

                class C1(object):
                    x = 23

                print C1.x
                #prints:23

            類對(duì)象 ``C1`` 有一個(gè)屬性,叫做 ``x`` ,它綁定了一個(gè)值 ``23`` ,而 ``C1.x`` 則指向這個(gè)屬性。

            你可以在類體外綁定或解除屬性。舉個(gè)例子:

            ::

                class C2(object):
                    pass

                C2.x = 23
                print C2.x
                #prints: 23

            但是,你的程序如果把綁定屬性之類的東西都放入類體中,這樣可讀性可能高一點(diǎn)。任何類屬性,都是隱式的被全部的實(shí)例共享,一旦實(shí)例被創(chuàng)建。這個(gè)我們后面很快就會(huì)討論。

            使用了 ``class`` 語句后,將會(huì)隱式的設(shè)置一些類屬性。屬性 ``__name__`` 是 ``classname`` 標(biāo)識(shí)符的字符串值。而屬性 ``__bases__`` 則是一個(gè)tuple,里面是各個(gè)給定的基類對(duì)象。舉個(gè)例子,使用剛才創(chuàng)建的 ``C1`` :

            ::

                print C1.__name__, C1.__base__
                #prints: C1, (<type 'object'>,)

            一個(gè)類還有個(gè)屬性 ``__dict__`` ,它是一個(gè)字典對(duì)象,類用它來存放所有其他的屬性。對(duì)于任意類對(duì)象 ``C`` ,任意對(duì)象 ``x`` ,和任意標(biāo)識(shí)符 ``s`` (除了 ``__name__`` , ``__bases__`` 和 ``__dict__`` ), ``c.s`` 是和 ``c.__dict__['s']=x`` 等價(jià)的。舉個(gè)例子,再次引用剛才創(chuàng)建的 ``C1`` :

            ::

                C1.y=45
                C1.__dict__['z'] = 67
                print C1.x, C1.y, C1.z
                #prints: 23, 45, 67

            以下的設(shè)定是沒有區(qū)別的:像在類體內(nèi)定義屬性,或是在類體外通過指定一個(gè)屬性,再或者,在類體外,顯式地綁定到 ``c.__dict__`` 。

            直接在類體中的語句,如果引用屬性時(shí),要使用簡單的名字,而不是完全的名字。舉個(gè)例子:

            ::

                class C3(object):
                    x = 23
                    y = x + 22
                    #must use just x,not C3.x

            但是呢,如果是在類體中所定義的方法內(nèi),引用時(shí)就必須使用完整的名字,而不是簡單的名字。舉個(gè)例子:

            ::

                class C4(object):
                    x = 23
                    def amethod(self):
                        print C4.x
                        # must use C4.x, not just x

            注意,使用屬性引用時(shí)(也就是像 ``c.s`` )比函數(shù)綁定有更多的語義。具體參考“Attribute Reference Basics”。

            5.1.3.2 Function definitions in a class body
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            大多數(shù)類體內(nèi)包含 ``def`` 語句,因?yàn)楹瘮?shù)(在此處被稱為方法)對(duì)于很多類對(duì)象來說都是很重要的屬性。一個(gè) ``def`` 語句盡管在類體中,但是仍然是和 “Function”的約定是一樣的。另外,在類體中的方法一直要有個(gè)強(qiáng)制的第一個(gè)參數(shù),約定稱為 ``self`` ,而他表示你調(diào)用方法所對(duì)應(yīng)的實(shí)例。在方法調(diào)用中, ``self`` 扮演一個(gè)很重要的角色。

            這里是一個(gè)例子,一個(gè)類包含一個(gè)方法:

            ::

                class C5(object):
                    def hello(self):
                        print "hello"

            一個(gè)類可以定義多個(gè)特殊方法(就是方法的名字前后都有兩個(gè)下劃線),而它們又與實(shí)例的特殊操作有關(guān)。我在“Special Methods”中有詳細(xì)討論。

            5.1.3.3 Class-private variables
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            當(dāng)一個(gè)類體中的語句(或者是一個(gè)方法)使用一個(gè)標(biāo)識(shí)符以兩個(gè)下劃線開頭(但不是結(jié)尾),比如就像 ``__ident`` ,python就會(huì)隱式地將標(biāo)識(shí)符轉(zhuǎn)成了 ``_classname__ident`` ,此次 ``classname`` 就是類的名字。這就可以使一個(gè)類使用私有的命名,以此減少無意中命名沖突的風(fēng)險(xiǎn)。

            根據(jù)約定,所有以單下劃線起始的標(biāo)識(shí)符,意味著對(duì)于綁定它們的作用域是私有的,不管這個(gè)作用域是不是一個(gè)類。python編譯器并不會(huì)強(qiáng)制進(jìn)行轉(zhuǎn)換;這完全取決于編程人員來遵守它。

            5.1.3.4 Class documentation strings
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            如果在類體中的第一條語句是一個(gè)字符串常量,那么編譯器會(huì)將其認(rèn)為是這個(gè)類的文檔。這個(gè)屬性叫做 ``__doc__`` 而且也被叫做一個(gè)類的 *docstring* 。更多地參考 “Docstring”。

            5.1.4 Descriptors
            -----------------

            一個(gè)描述符,就是一個(gè)新式的對(duì)象,而這個(gè)類提供了特殊的方法叫做 ``__get__`` 。描述符就是在語義上,控制一個(gè)實(shí)例屬性的訪問及設(shè)定。通俗的來講,就是當(dāng)你訪問一個(gè)實(shí)例的屬性時(shí),python會(huì)通過調(diào)用描述符的 ``__get__`` 來獲取屬性值。具體參考“Attribute Reference Basics”。

            5.1.4.1 Overriding and nonoverriding descriptors
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            如果一個(gè)描述符類還提供了特殊方法 ``__set__`` ,那么這個(gè)描述符就是所謂的 *overriding descripter* (或者,一個(gè)舊式的,比較混亂的術(shù)語, *data descriptor* );如果這個(gè)描述符類僅僅提供了 ``__get__`` ,而沒有 ``__set__`` ,那么這個(gè)描述符就是所謂的 *nonoverriding descriptor* (或者是 *nondata* )。舉個(gè)例子,一個(gè)函數(shù)對(duì)象的類如果提供了 ``__get__`` ,但沒有 ``__set__`` ;那么,這個(gè)函數(shù)對(duì)象就是個(gè) *nonoverriding descriptor* 。通俗地講,當(dāng)你指派一個(gè)值給一個(gè)實(shí)例的屬性,而它又是一個(gè) *overriding* 的描述符,那么python會(huì)調(diào)用 ``__set__`` 來賦值。
            具體的參考“Attributes of instance object”。

            舊式的類也可以有描述符,但是在舊式類中的描述符運(yùn)行起來就好像是 *nonoverriding* 一樣(它們的 ``__set__`` 方法,就會(huì)被忽略)。

            5.1.5 Instance
            --------------

            為創(chuàng)建一個(gè)類的實(shí)例,可以像調(diào)用函數(shù)一樣,調(diào)用那個(gè)類對(duì)象。每次調(diào)用都會(huì)返回一個(gè)新的實(shí)例,而它的類型就是那個(gè)類:

            ::

                anInstance=C5()

            你可以調(diào)用內(nèi)置函數(shù) ``isinstance(I,C)`` ,用一個(gè)類對(duì)象 ``C`` 作為參數(shù)。如果 ``isinstance`` 返回 ``True`` ,那么標(biāo)識(shí)實(shí)例 ``I`` 是類 ``C`` 的實(shí)例或是其子類。不然, ``isinstance`` 返回 ``False`` 。

            5.1.5.1 __init__
            ~~~~~~~~~~~~~~~~~~

            當(dāng)一個(gè)類定義了或繼承了一個(gè)名為 ``__init__`` 的方法,那么調(diào)用這個(gè)類對(duì)象時(shí),就會(huì)隱式地執(zhí)行 ``__init__`` 來初始化一個(gè)新的實(shí)例。而調(diào)用時(shí)傳入的參數(shù)必須要和 ``__init__`` 的參數(shù)一致,除了第一個(gè)參數(shù) ``self`` 。舉個(gè)例子,考慮下面的類:

            ::

                class C6(object):
                    def __init__(self,n):
                        self.x=n

            這里是如何創(chuàng)建 ``C6`` 的實(shí)例:

            ::

                anotherInstance = C6(42)

            就像在 ``C6`` 中展示的, ``__init__`` 方法一般含有綁定實(shí)例屬性的語句。而且,一個(gè) ``__init__`` 方法不能夠返回一個(gè)值;否則,python會(huì)拋出一個(gè) ``TypeError`` 異常。

            方法 ``__init__`` 的目的,是為了綁定,從而創(chuàng)建新建實(shí)例的屬性。你也可以綁定或解除實(shí)例的屬性在 ``__init__`` 的外面,就像你等等會(huì)看到的。但是,你的代碼會(huì)有更好的可讀性,如果你在 ``__init__`` 方法中初始化所有屬性的值。

            當(dāng) ``__init__`` 是缺省的,那么你調(diào)用時(shí)不能給定參數(shù)。而且,新生成的實(shí)例沒有因?qū)嵗厥獾膶傩浴?/p>

            5.1.5.2 Attributes of instance objects
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            一旦你創(chuàng)建了一個(gè)實(shí)例,那么你就可以訪問這個(gè)屬性(數(shù)據(jù)和方法)通過使用點(diǎn)( ``.`` )操作符。舉個(gè)例子:

            ::

                anInstance.hello()
                #prints: Hello
                anotherInstance.x
                #prints: 42

            屬性引用有很豐富的語義,具體參考“Attribute Reference Basics”。

            你可以給一個(gè)實(shí)例對(duì)象任意的屬性,通過綁定一個(gè)值給一個(gè)屬性引用。舉個(gè)例子:

            ::

                class C7:
                    pass

                z = C7()
                z.x = 23
                print z.x
                #prints: 23

            實(shí)例對(duì)象 ``z`` 現(xiàn)在就有一個(gè)名為 ``x`` 的屬性,綁定了值 ``23`` ,而 ``z.x`` 則指向那個(gè)屬性。注意特殊方法 ``__setattr__`` ,如果存在時(shí),將會(huì)偵聽每個(gè)綁定屬性的行為。此外,對(duì)于一個(gè)新式的實(shí)例,如果你嘗試綁定屬性,而這個(gè)屬性又是一個(gè) *overriding* 描述符,那么這個(gè)描述符的 ``__set__`` 將會(huì)監(jiān)聽。在這種情況下,語句 ``z.x=23`` 其實(shí)是執(zhí)行了 ``type(z).x.__set__(z,23)`` (舊式的實(shí)例將會(huì)忽略描述符的這個(gè)特性,也就是說,它們從未調(diào)用它們的 ``__set__`` 方法)。

            創(chuàng)建一個(gè)實(shí)例時(shí)將隱式地設(shè)置兩個(gè)實(shí)例屬性。對(duì)于任何實(shí)例 ``z`` , ``z.__class__`` 是 ``z`` 所屬的類對(duì)象,而 ``z.__dict__`` 則是一個(gè)字典用來存放它的其它屬性。例如,對(duì)于實(shí)例 ``z`` 我們這樣創(chuàng)建:

            ::

                print z.__class__.__name__, z.__dict__
                #prints: C7, {'x':23}

            你可能重新綁定(但不是解除)任意或全部的屬性,但是這很少是必需的。一個(gè)新式實(shí)例的 ``__class__`` 僅僅可能被重綁定到新式類上,而且一個(gè) *legacy* 實(shí)例的 ``__class__`` 僅能綁定到 *legacy* 的類上。

            對(duì)于任何實(shí)例 ``z`` ,對(duì)象 ``x`` ,和標(biāo)識(shí)符 ``s`` (除了 ``__class__`` 和 ``__dict__`` ), ``z.s=x`` 等價(jià)于 ``z.__dict__['s']=x`` (除非有個(gè)特殊方法 ``__setattr__`` ,或者是一個(gè) *overriding* 描述符的特殊方法 ``__set__`` ,將會(huì)監(jiān)聽嘗試綁定的行為)。舉個(gè)例子,重新使用剛才創(chuàng)建的 ``z`` :

            ::

                z.y = 45
                z.__dict__['z'] = 67
                print z.x, z.y, z.z
                #prints: 23, 45, 67

            在實(shí)例創(chuàng)建屬性時(shí)使用 ``__init__`` 和顯示使用 ``z.__dict__`` 是一樣的。

            5.1.5.3 The factory-function idiom
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            一個(gè)常見的任務(wù)是根據(jù)一些條件來創(chuàng)建不同類的實(shí)例,或者是防止創(chuàng)建一個(gè)新實(shí)例,假如已經(jīng)存在一個(gè)可復(fù)用的。一個(gè)常見的誤解就是,在 ``__init__`` 應(yīng)該返回一個(gè)對(duì)象,但是這是很難實(shí)現(xiàn)的:python會(huì)產(chǎn)生異常,當(dāng)返回了任何值而不是 ``None`` 。最好的方式來執(zhí)行一個(gè)靈活對(duì)象的創(chuàng)建,是使用普通的函數(shù),而不是直接調(diào)用這個(gè)類對(duì)象。一個(gè)扮演這個(gè)角色的函數(shù)稱為一個(gè) *factory function* 。

            調(diào)用一個(gè) *factory function* 是一個(gè)很靈活的方法:一個(gè)函數(shù)可能會(huì)返回一個(gè)已存在可復(fù)用的實(shí)例,或者是創(chuàng)建一個(gè)新的實(shí)例通過調(diào)用任何適合的類。加入你有兩個(gè)差不多可交換的類( ``SpecialCase`` 和 ``NormalCase`` ),而你希望依據(jù)參數(shù),靈活地創(chuàng)建合適的實(shí)例。下面的 *factory function* ``appropriateCase`` 就允許你做這樣的事情:

            ::

                class SpecialCase(object):
                    def amethod(self):
                        print "special"

                class NormalCase(object):
                    def amethod(self):
                        print "normal"

                def appropriateCase(isnormal=True):
                    if isnormal:
                        return NormalCase()
                    else:
                        return SpecialCase()

                aninstance = appropriateCase(isnormal=False)
                aninstance.amethod()
                #prints "special", as desired

            5.1.5.4 __new__
            ~~~~~~~~~~~~~~~

            每一個(gè)新式類都有(或繼承了)一個(gè)靜態(tài)方法( **static method** ) ``__new__`` 。當(dāng)你調(diào)用 ``C(*args,**kwds)`` 來創(chuàng)建類 ``C`` 的新實(shí)例時(shí),Python會(huì)首先調(diào)用 ``C.__new__(C,*args,**kwds)`` 。Python使用 ``__new__`` 的返回值 ``x`` 作為新創(chuàng)建的實(shí)例。然后,python會(huì)再調(diào)用 ``C.__init__(x,*args,**kwds)`` ,但是僅僅當(dāng) ``x`` 確實(shí)是 ``C`` 或它子類的實(shí)例時(shí)(不然,``x`` 的狀態(tài)仍處于 ``__new__`` 留給它的 )。因此,舉個(gè)例子,語句 ``x=C(23)`` 等同于:

            ::

                x = C.__new__(C,23)
                if isinstance(x,C):
                    type(x).__init__(x,23)

            這里, ``object.__new__`` 將創(chuàng)建一個(gè)新的,為初始化的實(shí)例,并且接受這個(gè)類作為第一個(gè)參數(shù)。它將會(huì)忽略其他的參數(shù),如果它有一個(gè) ``__init__`` 方法。但是如果它接受其他參數(shù)在這個(gè)參數(shù)之前,那么將會(huì)產(chǎn)生一個(gè)異常。當(dāng)然如果沒有 ``__init__`` 方法也是會(huì)拋出異常的。當(dāng)你在類體中重載了 ``__new__`` ,你不需要增加 ``__new__=staticmethod(__new__)`` ,就如你一般會(huì)喜歡的:python會(huì)識(shí)別這個(gè)名字 ``__name__`` 并且在它的上下文特殊地處理它。在那些不常有的情況下,如果你在類體外面重新綁定了 ``C.__new__`` 那么你就確實(shí)需要使用 ``C.__new__=staticmethod(whatever)`` 。

            對(duì)于 ``__new__`` 有著大部分 **factory function** 的靈活性。 ``__new__`` 將會(huì)適時(shí)地選擇返回一個(gè)已存在的實(shí)例或創(chuàng)建一個(gè)新的實(shí)例。當(dāng) ``__new__`` 確實(shí)需要?jiǎng)?chuàng)建一個(gè)新的實(shí)例,它經(jīng)常會(huì)把創(chuàng)建的任務(wù)委托給 ``object.__new__`` 的調(diào)用或者是它其他父類的 ``__new__`` 的調(diào)用。下面的例子演示了如何重載靜態(tài)方法 ``__new__`` 為了實(shí)現(xiàn)一個(gè) **Singleton** 設(shè)計(jì)模式:

            ::

                class Singleton(object):
                    _singletons={}
                    def __new__(cls,*args,**kwds):
                        if cls not in cls._singletons:
                            cls._singletons[cls] = super(Singleton,cls).__new__(cls)
                        return cls._singletons[cls]

            任何 ``Singleton`` 的子類(當(dāng)然沒有重載 ``__new__`` ),就將只有一個(gè)實(shí)例。如果一個(gè)子類定義了一個(gè) ``__init__`` 方法,這個(gè)子類必須確保它的 ``__init__`` 是安全的,當(dāng)被重復(fù)調(diào)用時(shí)(在每次的創(chuàng)建請(qǐng)求下)。

            舊式的類是沒有 ``__new__`` 方法的。

            5.1.6 Attribute Reference Basics
            --------------------------------

            一個(gè)屬性引用就是形如 ``x.name`` 的表達(dá)式,這里 ``x`` 可以是任何表達(dá)式,而 ``name`` 是一個(gè)被稱為 *attribute name* 的標(biāo)識(shí)符。很多種python對(duì)象都具有屬性,但是一個(gè)熟悉引用在 ``x`` 指向一個(gè)類或?qū)嵗龝r(shí)具有特殊的豐富的語義。記住,方法也是屬性,所以任何我們所謂的屬性同樣也適用于可調(diào)用的屬性(比如說方法)。

            假如 ``x`` 是一個(gè)類 ``C`` 的實(shí)例,而它又是繼承自基類 ``B`` 。兩個(gè)類和實(shí)例有一些屬性(數(shù)據(jù)和方法),就像下面的:

            ::

                class B(object):
                    a = 23
                    b = 45
                    def f(self):
                        print "method f in class B"
                    def g(self):
                        print "method g in class B"

                class C(B):
                    b = 67
                    c = 89
                    d = 123
                    def g(self):
                        print "method g in class C"
                    def h(self):
                        print "method h in class C"

                x = C()
                x.d = 77
                x.e = 88

            一些屬性名字是特殊的。舉個(gè)例子, ``C.__name__`` 是字符串 ``'C'`` 而且是這個(gè)類名稱。 ``C.__bases__`` 則是一個(gè)元組 ``(B,)`` ,這是類 ``C`` 的基類。 ``x.__class__`` 是類 ``C`` ,它是 ``x`` 所屬的類。當(dāng)你指向這些特殊的屬性,這個(gè)屬性引用將會(huì)訪問給定的位置,然后把它找到的值拿出來。你不能解除這些屬性。重新綁定是允許的,所以你可以改變名字或者是這個(gè)類的基類,或者是實(shí)例的類對(duì)象,匆忙地,但是這個(gè)高級(jí)的技術(shù)非同尋常的重要。

            類 ``C`` 和實(shí)例 ``x`` 都各自有一個(gè)其他的特殊屬性:一個(gè)叫做 ``__dict__`` 的字典。全部其他的屬性,除了僅有的幾個(gè)特殊屬性外,全部都被放在類或?qū)嵗?``__dict__`` 中。

            5.1.6.1 Getting an attribute from a class
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            當(dāng)你使用語法 ``C.name`` 來指向一個(gè)類 ``C`` 上的屬性,搜索會(huì)分成兩步進(jìn)行:

            1. 如果 ``name`` 就在 ``C.__dict__`` 中,那么 ``C.name`` 將會(huì)從 ``C.__dict__['name']`` 中取得值 ``v`` 。然后,如果 ``v`` 是一個(gè)描述符(也就是說, ``type(v)`` 提供了名為 ``__get__`` 的方法), ``C.name`` 的值將是調(diào)用 ``type(v).__get__(v,None,C)`` 的結(jié)果。否則, ``C.name`` 的值就是 ``v`` 。
            2. 否則, ``C.name`` 就委托給 ``C`` 的基類來查找,意味著它會(huì)沿著 ``C`` 的祖先一個(gè)個(gè)查找 ``name`` (關(guān)于其查找順序,參考1.8.1)。

            5.1.6.2 Getting an attribute from an instance
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            但你使用 ``x.name`` 來指向類 ``C`` 的實(shí)例 ``x`` ,搜索會(huì)分成三步進(jìn)行:

            1. 當(dāng)在 ``C`` (或者是在 ``C`` 的祖先類中)找到了 ``name`` ,作為一個(gè) *overriding* 描述符 ``v`` 的名字(也就是說, ``type(v)`` 提供了 ``__get__`` 和 ``__set__`` 方法), ``C.name`` 的值將是調(diào)用 ``type(v).__get__(v,x,C)`` 的結(jié)果。(此步不適用于舊式實(shí)例)。
            2. 否則,當(dāng) ``name`` 是在 ``x.__dict__`` 中的鍵值,那么 ``x.name`` 將會(huì)獲取并返回 ``x.__dict__['name']`` 的值。
            3. 否則, ``x.name`` 將搜索委托給 ``x`` 的類(和搜索 ``C.name`` 的兩步是一樣的,就像剛才所說的 )。如果一個(gè)描述符 ``v`` 被找到了,那么屬性搜尋的結(jié)果,又將是 ``type(v).__get__(v,x,C)`` ;如果是一個(gè)非描述符值 ``v`` 被找到,那么結(jié)果就是 ``v`` 。

            當(dāng)這些搜索步驟結(jié)束后沒有找到屬性,python就會(huì)產(chǎn)生一個(gè) ``AttributeError`` 的異常。但是,對(duì)于 ``x.name`` 的搜索,如果 ``C`` 定義或者繼承了特殊方法 ``__getattr__`` ,python將會(huì)調(diào)用 ``C.__getattr__(x,'name')`` 而不是產(chǎn)生一個(gè)異常(這完全取決于 ``__getattr__`` 是返回一個(gè)合適的值還是產(chǎn)生一個(gè)合適的異常,經(jīng)常是 ``AttributeError`` )。

            考慮下面的屬性引用:

            ::

                print x.e, x.d, x.c, x.b, x.a
                #prints: 88, 77, 89, 67, 23

            這里的 ``x.e`` 和 ``x.d`` 在實(shí)例搜尋的第二步就成功了,因?yàn)闆]有涉及描述符,而且 ``e`` 和 ``d`` 都是在 ``x.__dict__`` 中的鍵值。所以,搜索不會(huì)繼續(xù),而是返回 ``88`` 和 ``77`` 。而其他三個(gè)則是到第三步,并且搜索了 ``x.__class__`` (也就是 ``C`` )。 ``x.c`` 和 ``x.b`` 是在類搜尋的第一步就成功了,因?yàn)?``c`` 和 ``b`` 是在 ``C.__dict__`` 中。所以它們就返回了 ``89`` 和 ``67`` 。而 ``x.a`` 則是直到類搜索的第二步,搜索 ``C.__bases__[0]`` (也就是 ``B`` )。 ``a`` 是 ``B.__dict__`` 的鍵值,所以 ``x.a`` 終于成功并且返回 ``23`` 。

            5.1.6.3 Setting an attribute
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            注意屬性搜尋的步驟,只有在你引用它時(shí)才會(huì)發(fā)生,而不是在你綁定一個(gè)屬性的時(shí)候。當(dāng)你綁定(不管是類還是實(shí)例)一個(gè)屬性時(shí),而它的名字不是特殊的(除非有個(gè) ``__setattr__`` 方法,或者是一個(gè) *overriding* 描述符的 ``__set__`` 方法,截取一個(gè)實(shí)例屬性的綁定),你僅僅影響了在 ``__dict__`` 中的屬性(在類或?qū)嵗校?。換句話說,在屬性綁定的情況下,是沒有搜索被執(zhí)行的,除了檢查是否是個(gè) *overriding* 描述符。

            5.1.7 Bound and Unbound Methods
            -------------------------------

            一個(gè)函數(shù)對(duì)象的 ``__get__`` 方法返回一個(gè)包裝了函數(shù)的 *unbound method object* 或 *bound method object* 。在兩者間的不同點(diǎn)在于,一個(gè) **unbound method** 沒有和一個(gè)特別的實(shí)例綁定,而 **bound method** 則有。

            在前面段落的代碼中,屬性 ``f`` , ``g`` 和 ``h`` 都是函數(shù);所以,對(duì)于任一的屬性引用將會(huì)返回一個(gè)包裝了相應(yīng)函數(shù)的方法對(duì)象??紤]下面的:

            ::

                print x.h, x.g, x.f, C.h, C.g, C.f

            這個(gè)語句將輸出三個(gè)綁定方法,像這樣:

            ::

                <bound method C.h of <__main__.C object at 0x8156d5c>>

            然后是像這樣的三個(gè)未綁定方法:

            ::

                <unbound method C.h>

            當(dāng)我們引用實(shí)例 ``x`` 的屬性時(shí)將返回綁定方法,而引用類 ``C`` 的屬性時(shí)則返回未綁定的方法。

            因?yàn)橐粋€(gè)綁定的方法已經(jīng)和一個(gè)特殊的實(shí)例綁定了,你可以這樣調(diào)用方法:

            ::

                x.h()
                #prints: method in class C

            這里要注意的就是,你不需要傳給方法第一個(gè)參數(shù), ``self`` 。對(duì)于實(shí)例 ``x`` 的綁定方法會(huì)隱式地把對(duì)象 ``x`` 綁定給參數(shù) ``self`` 。因此,方法的主體能夠訪問實(shí)例的屬性,作為 ``self`` 的屬性,盡管我們沒有顯示的傳遞給方法這個(gè)參數(shù)。

            一個(gè)未綁定的方法,是沒有和一個(gè)特殊實(shí)例關(guān)聯(lián)的,所以你必須指定一個(gè)合適的對(duì)象給第一個(gè)參數(shù),當(dāng)你調(diào)用一個(gè)未綁定方法,舉個(gè)例子:

            ::

                C.h(x)
                #prints: method h in class C

            你調(diào)用一個(gè)未綁定的方法比綁定方法次數(shù)要少的多。未綁定方法的主要用途是來訪問 *overridden* 方法;更好的是使用 ``super`` 。

            5.1.7.1 Unbound method details
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            就像剛才我們討論的,當(dāng)一個(gè)屬性引用是從類指向函數(shù)時(shí),返回一個(gè)引用指向那個(gè)包裹了函數(shù)的未綁定方法。一個(gè)未綁定方法除了包裝的函數(shù)外還有三個(gè)額外的屬性: ``im_class`` 是提供方法的類對(duì)象, ``im_func`` 是被包裝的函數(shù),而 ``im_self`` 通常返回 ``None`` 。這些屬性是只讀的,這意味著嘗試重綁定或解除綁定都會(huì)拋出異常。

            你可以調(diào)用一個(gè)未綁定的方法就如同你調(diào)用了它的 ``im_func`` 函數(shù),但是第一個(gè)參數(shù)必須是是 ``im_class`` 的實(shí)例或是后裔。換句話說,對(duì)于未綁定方法的調(diào)用,必須至少有一個(gè)參數(shù),這個(gè)參數(shù)應(yīng)該和被包裝的函數(shù)的第一個(gè)參數(shù)一致(一般稱為 ``self`` )。

            5.1.7.2 Bound method details
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            當(dāng)一個(gè)屬性引用自一個(gè)實(shí)例,在搜索中,找到一個(gè)函數(shù)對(duì)象,而那是一個(gè)實(shí)例的類中的屬性,查找會(huì)調(diào)用函數(shù)的 ``__get__`` 方法來獲取屬性值。這個(gè)調(diào)用,在這種情況下,創(chuàng)建并返回了一個(gè)綁定的方法,并包裹著那個(gè)函數(shù)。

            注意當(dāng)一個(gè)屬性引用的查找在 ``x.__dict__`` 中找到了函數(shù)對(duì)象,那么屬性引用操作不會(huì)創(chuàng)建一個(gè)綁定方法,因?yàn)樵谶@種情況下函數(shù)并不是當(dāng)作一個(gè)描述符,而且,函數(shù)的 ``__get__`` 是不可調(diào)用的;更準(zhǔn)確地說,函數(shù)對(duì)象本身就是屬性值。類似的,沒有一個(gè)綁定的方法是為不普通的函數(shù)調(diào)用而創(chuàng)建的,就如內(nèi)置的(與python代碼對(duì)照)函數(shù),因?yàn)樗鼈儾皇敲枋龇?/p>

            一個(gè)綁定方法,和一個(gè)未綁定方法是類似的,它們都有三個(gè)只讀的屬性,那是包裝函數(shù)對(duì)象多出來的。像在未綁定方法中, ``im_class`` 是一個(gè)提供方法的類對(duì)象,而 ``im_func`` 是被包裝的函數(shù)。但是,在一個(gè)綁定方法的對(duì)象中,屬性 ``im_self`` 指向獲得方法的實(shí)例 ``x`` 。

            一個(gè)綁定方法使用起來像它的 ``im_func`` 函數(shù),但是調(diào)用綁定方法不需要顯式提供第一個(gè)參數(shù)(一般約定為 ``self`` )。當(dāng)你調(diào)用一個(gè)綁定方法,在傳遞其他參數(shù)時(shí),綁定方法把 ``im_self`` 傳遞給 ``im_func`` 的第一個(gè)參數(shù)。

            讓我們看看下面這個(gè)底層的概念上的是如何調(diào)用 ``x.name(arg)`` 。在下面的例子中:

            ::

                def f(a,b):
                    ...
                    # a function f with two arguments

                class C(object):
                    name = f

                x = C()

            這里 ``x`` 是一個(gè)類 ``C`` 的實(shí)例對(duì)象, ``name`` 是一個(gè)標(biāo)識(shí)符,指明了 ``x`` 的方法( ``C`` 的屬性,它是一個(gè)函數(shù),在這里是函數(shù) ``f`` ),而 ``arg`` 是任何的表達(dá)式。python先檢查 ``name`` 是否是 ``C`` 中的屬性,并且它還是一個(gè)描述符。但是事實(shí)上它并不是,因?yàn)樗鼈兊念愲m然定義了 ``__get__`` 方法,但是并不是 *overriding* 的描述符,因?yàn)樗鼈儧]有 ``__set__`` 方法。python接下來檢查 ``name`` 是否在 ``x.__dict__`` 中。但它們也不是。所以python在類 ``C`` 中查找 ``name`` (任何東西都會(huì)起作用以同樣的方式如果 ``name`` 被找到了,通過繼承,在 ``C`` 的 ``__bases__`` 中)。python注意到這個(gè)屬性值,也就是函數(shù)對(duì)象 ``f`` ,是一個(gè)描述符。所以,python調(diào)用 ``f.__get__(x,C)`` ,這就創(chuàng)建了一個(gè)綁定方法, ``im_func`` 設(shè)定為 ``f`` , ``im_class`` 被設(shè)定為 ``C`` ,而 ``im_self`` 被設(shè)為 ``x`` 。然后python調(diào)用這個(gè)綁定方法對(duì)象,以 ``arg`` 作為唯一的參數(shù)。這個(gè)綁定方法插入了 ``im_self`` (也就是 ``x`` )作為第一個(gè)參數(shù),而 ``arg`` 成為第二個(gè),然后調(diào)用 ``im_func`` (也就是 ``f`` )??偟男Ч拖袷沁@樣調(diào)用:

            ::

                x.__class__.__dict__['name'](x,arg)

            當(dāng)一個(gè)綁定方法的函數(shù)體執(zhí)行了,它沒有特別的命名空間,不管是 ``self`` 或其他類。變量引用的是局部或全局的,就像其他的函數(shù)一樣。變量并不會(huì)隱式的指明 ``self`` 的屬性,或是任何其他類中的屬性。當(dāng)一個(gè)方法需要指向,綁定,或解除綁定一個(gè) ``self`` 對(duì)象的屬性,它也需要標(biāo)準(zhǔn)的屬性引用的語法(比如, ``self.name`` )。隱式的作用域可能會(huì)導(dǎo)致使用其他用過的,而python則不是這樣(因?yàn)閜ython和其他面向?qū)ο蟛煌沁@使得python簡潔明了,而避免了混淆。

            綁定方法對(duì)象是一級(jí)對(duì)象,你可以在任何你可以使用可調(diào)用對(duì)象的地方使用它們。因?yàn)榻壎ǚ椒ǔ钟幸粋€(gè)包裝函數(shù)的引用,并且指向它所執(zhí)行的 ``self`` 對(duì)象,它是更有用更靈活的選擇對(duì)應(yīng)嵌套的來說。一個(gè)實(shí)例對(duì)象的類提供了特殊的方法 ``__call__`` 提供了另一個(gè)可行的選擇。這些概念中的每一個(gè)使你綁定一些行為(代碼)和狀態(tài)(數(shù)據(jù))到一個(gè)單獨(dú)的可調(diào)用對(duì)象中。嵌套可能是最簡單的,但有很多的限制。這里是嵌套:

            ::

                def make_adder_as_closure(augend):
                    def add(addend, _augend=augend):
                        return addend+_augend
                    return add

            綁定方法和可調(diào)用的實(shí)例更加的豐富和靈活。這里是用綁定方法實(shí)現(xiàn)同樣的功能:

            ::

                def make_adder_as_bound_method(augend):
                    class Adder(object):
                        def __init__(self,augend):
                            self.augend = augend
                        def add(self, addend):
                            return addend+self.augend
                    return Adder(augend).add

            這里是以一個(gè)可調(diào)用實(shí)例來執(zhí)行(這個(gè)實(shí)例的類提供了特殊方法 ``__call__`` ):

            ::

                def make_adder_as_callable_instance(augend):
                    class Adder(object):
                        def __init__(self,augend):
                            self.augend=augend
                        def __call__(self,addend):
                            return addend+self.augend
                    return Adder(augend)

            從這些代碼調(diào)用函數(shù)來看,它們是可交換的,因?yàn)樗鼈兌挤祷囟鄳B(tài)的可調(diào)用對(duì)象(也就是都是可用的)。在這里,嵌套是最簡單的;而其他兩種則是更靈活,普遍而且更強(qiáng)大的機(jī)制,但是在這個(gè)簡單的例子中沒必要使用這么強(qiáng)大的功能。

            5.1.8 Inheritance
            -----------------

            當(dāng)你使用一個(gè)屬性引用 ``C.name`` ,但是 ``name`` 不在 ``C.__dict__`` 中,搜尋會(huì)隱式的在 ``C.__bases__`` 以特殊的順序執(zhí)行(歷史原因,稱為 *method resolution order* ,或者是 **MRO** ,甚至是對(duì)于任何屬性,而不僅僅是對(duì)方法)。 ``C`` 的基類會(huì)又會(huì)依次的搜尋它們的基類。搜尋機(jī)制檢查直接或間接的祖先,一個(gè)又一個(gè),以 **MRO** 進(jìn)行,當(dāng) ``name`` 被找到時(shí)將會(huì)停止。

            5.1.8.1 Method resolution order
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            在一個(gè)類中,屬性名字的搜尋本質(zhì)上通過以 **left-to-right** , **depth-first** 的順序訪問祖先發(fā)生。但是,在多繼承的存在下(這樣就使繼承的圖表是一個(gè)非循環(huán)的圖表,而不是一個(gè)樹形的),這個(gè)簡單的方法可能導(dǎo)致一些祖先類被訪問了兩次。在這樣的情況下,這種分解的順序被闡明,通過在搜尋的列表留下右邊的那個(gè)類,這將會(huì)持續(xù)到搜索完,然后再去剛才剩下的那些類中重新開始搜索。這個(gè)就使得多類繼承很難正確有效的使用。而新式對(duì)象則在此方面更加高級(jí)。

            這在 **left-right** , **depth-first** 搜索中個(gè)問題,可以很簡單的在舊式類中被論證:

            ::

                class Base1:
                    def amethod(self):
                        print "Base1"

                class Base2(Base1):
                    pass

                class Base3:
                    def amethod(self):
                        print "Bases3"

                class Derived(Base2,Base3):
                    pass

                aninstance=Derived()
                aninstance.amethod()
                #prints: "Base1"

            在這個(gè)情況下, ``amethod`` 的搜尋將從 ``Derived`` 開始。當(dāng)沒有在此處找到,將會(huì)去搜索 ``Base2`` 。因?yàn)樵诖颂幰矝]有找到,舊式的搜尋將會(huì)繼續(xù)搜索它的父類, ``Base1`` ,而在這里找到了那個(gè)屬性。所以,就是類將會(huì)停止,而不在考慮 ``Base3`` ,而在這里也可以找到那個(gè)屬性。而新式的 **MRO** 則解決了那個(gè)問題,通過移除最左邊的 ``Base1`` ,所以將會(huì)搜索 ``Base3`` 中的 ``amethod`` 。

            Figure 5-1 展示了舊式和新式的 **MRO** ,在這個(gè)菱形的繼承圖中。

            .. figure:: pythonian_0501.jpg
                :align: center

                **Figure 5-1. Legacy and new-style MRO**


            每一個(gè)新式的類和內(nèi)置類型都有一個(gè)特殊制度的類屬性,叫做 ``__mro__`` ,這是一個(gè)用于方法分解的元組,以一定順序存放類型。你只可以在類中引用 ``__mro__`` ,而不以在實(shí)例上,而且因?yàn)?``__mro__`` 是只讀的屬性,你不可以重新綁定或解除綁定。具體的,你可以參考 Michele Simionato 寫的 “The Python 2.3 Method Resolution Order” ,在 http://www.python.org/2.3/mro.html 。

            5.1.8.2 Overriding attributes
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            就像我們剛看到的,一個(gè)屬性的搜尋會(huì)依照 **MRO** (典型的是根據(jù)繼承樹)然后在找到時(shí)就會(huì)停止。而派生類會(huì)比他們的父類先被搜尋,這樣子,當(dāng)子類和父類都定義了相同名字的屬性時(shí),搜索就會(huì)在子類中尋到定義并在那里停止。這就是所謂的 **覆寫** 了父類中的定義??紤]下面的例子:

            ::

                class B(object):
                    a=23
                    b=45
                    def f(self):
                        print "method f in class B"
                    def g(self):
                        print "method g in class B"

                class C(B):
                    b=67
                    c=89
                    d=123
                    def g(self):
                        print "method g in class C"
                    def h(self):
                        print "method h in class C"

            在這段代碼中,類 ``C`` 覆寫了超類 ``B`` 的屬性 ``b`` 和 ``g`` 。注意,不像其他的語言,python中你可以覆寫數(shù)據(jù)屬性,也可以是可調(diào)用的屬性(方法)。

            5.1.8.3 Delegating to superclass methods
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            當(dāng)一個(gè)子類 ``C`` 覆寫了它的超類 ``B`` 中的一個(gè)方法 ``f`` 時(shí), ``C.f`` 的主體經(jīng)常需要把某些操作委托給超類中的方法來執(zhí)行。這個(gè)可以使用未綁定方法,像下面:

            ::

                class Base(object):
                    def greet(self,name):
                        print "Welcome ",name

                class Sub(Base):
                    def greet(self,name):
                        print "Well Met and",
                        Base.greet(self,name)

                x=Sub()
                x.greet('Alex')

            在 ``Sub.greet`` 的主體中,超類的委托,通過屬性引用 ``Base.greet`` 使用了未綁定方法,所以正常的傳遞了所有的屬性,包括 ``self`` 。未綁定方法最常用的就是在委托超類執(zhí)行中。

            一個(gè)很常見的委托就是在特殊方法 ``__init__`` 中。當(dāng)python創(chuàng)建一個(gè)實(shí)例時(shí),基類的 ``__init__`` 方法不會(huì)自動(dòng)調(diào)用,就像在其他一些面向?qū)ο蟮恼Z言中一樣。因此,必要時(shí),就依靠于使用委托來完成合適的初始化。舉個(gè)例子:

            ::

                class Base(object):
                    def __init__(self):
                        self.anattribute = 23

                class Derived(Base):
                    def __init__(self):
                        Base.__init__(self)
                        self.anotherattribute=45

            如果在類 ``Derived`` 的 ``__init__`` 中未顯式調(diào)用類 ``Base`` , ``Derived`` 的實(shí)例將會(huì)丟失初始化的部分信息,所以他將缺少屬性 ``anotherattribute`` 。

            5.1.8.4 Cooperative superclass method calling
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            使用未綁定方法調(diào)用超類的方法,可能在多重繼承上出問題,特別是菱形狀的。考慮下面的定義:

            ::

                class A(object):
                    def met(self):
                        print 'A.met'

                class B(A):
                    def met(self):
                        print 'B.met'
                        A.met(self)

                class C(A):
                    def met(self):
                        print 'C.met'
                        A.met(self)

                class D(B,C):
                    def met(self):
                        print 'D.met'
                        B.met(self)
                        C.met(self)

            在這個(gè)代碼中,當(dāng)我們調(diào)用 ``D().met()`` , ``A.met`` 將會(huì)出現(xiàn)兩次。那我們?nèi)绾未_保祖先只被調(diào)用一次,而且僅僅一次?解決的辦法就是,使用內(nèi)置的 ``super`` 。 ``super(aclass,obj)`` ,將會(huì)返回對(duì)象 ``obj`` 的特殊超級(jí)對(duì)象。當(dāng)我們搜尋一個(gè)屬性(比如一個(gè)方法)在這個(gè)超級(jí)對(duì)象中,搜尋將會(huì)開始于在 ``obj`` 的 **MRO** 的類 ``aclass`` 中。所以我們可以這樣改寫先前的代碼:

            ::

                class A(object):
                    def met(self):
                        print 'A.met'

                class B(A):
                    def met(self):
                        print 'B.met'
                        super(B,self).met()

                class C(A):
                    def met(self):
                        print 'C.met'
                        super(C,self).met()

                class D(B,C):
                    def met(self):
                        print 'D.met'
                        super(D,self).met()

            現(xiàn)在, ``D().met()`` 對(duì)每個(gè)類的 ``met`` 都只調(diào)用了一次。如果你形成了在使用超類時(shí)使用 ``super`` 的習(xí)慣,那么盡管可能有很復(fù)雜的結(jié)構(gòu),你的類也能很好的使用。無論這個(gè)繼承結(jié)果有多簡單,使用這個(gè)并沒有壞處,而且,我也推薦使用新式的對(duì)象模型。

            你使用未綁定方法的技術(shù)的唯一的情況,可能就是,類之間的方法有不相容的簽名,但如果你真的要處理這種東西,那么使用未綁定方法的技術(shù)可能會(huì)至少有點(diǎn)討厭。多重繼承的合適使用可能會(huì)有限制。但是,就算OOP的大多數(shù)基本的特征,像在基類與子類間的多態(tài)將會(huì)因?yàn)楹灻牟灰恢聦?dǎo)致有所削減。

            5.1.8.5 "Deleting" class attributes
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            繼承和覆寫提供了簡單有效的方式來增加或修改類屬性(特別是方法)但不帶侵略性的(也就是說,不會(huì)修改那個(gè)定義了屬性的類)通過在子類中增加或覆寫屬性。但是,繼承并不提供來刪除或隱藏基類的屬性的方式但不帶侵略性的。如果子類定義或覆寫一個(gè)屬性失敗了,python就會(huì)尋找基類的定義。如果你需要執(zhí)行這樣的刪除,那么可能包含:

            * 覆寫方法并在方法內(nèi)產(chǎn)生異常。
            * 避開繼承,擁有這個(gè)屬性在其他地方而不是在子類的 ``__dict__`` ,而且定義 ``__getattr__`` 來有選擇的委托。
            * 使用新式對(duì)象模型,而且覆寫 ``__getattribute__`` 實(shí)現(xiàn)相同的效果。

            5.1.9 The Built-in object Type
            ------------------------------

            內(nèi)置的 ``object`` 類型是全部內(nèi)置類型和新式類的祖先。這個(gè) ``object`` 類型定義了一些特殊的方法,它們執(zhí)行對(duì)象的默認(rèn)語義。

            ``__new__`` ``__init__``
                你可以創(chuàng)建一個(gè) ``object`` 的直接實(shí)例,通過不帶參數(shù)的調(diào)用 ``object()`` 。調(diào)用將會(huì)隱式地使用 ``object.__new__`` 和 ``object.__init__`` 來創(chuàng)建和返回一個(gè)沒有屬性的實(shí)例對(duì)象(甚至沒有存放屬性的 ``__dict__`` )。這個(gè)實(shí)例對(duì)象就像一個(gè)“哨兵”一樣有用,保證不和其他不同的對(duì)象不相同。

            ``__delattr__`` ``__getattribute__`` ``__setattr__``
                默認(rèn)的,一個(gè)對(duì)象操作屬性引用通過使用 ``object`` 的這些方法。

            ``__hash__`` ``__repr__`` ``__str__``
                任意對(duì)象可以傳遞給函數(shù) ``hash`` 和 ``repr`` 和傳給類型 ``str`` 。

            一個(gè) ``object`` 的子類會(huì)覆寫這些方法或增加其他。

            5.1.10 Class-Level Methods
            --------------------------

            Python提供了兩種內(nèi)置的 **nonoverriding** 描述符類型,這就給了一個(gè)類兩種不同的“類級(jí)方法(classlevel method)”。

            5.1.10.1 Static methods
            ~~~~~~~~~~~~~~~~~~~~~~~

            一個(gè) **static method** 是一個(gè)方法,你可以在類上或者在類的任意實(shí)例調(diào)用,但沒有特殊的行為和普通方法的約束,綁定和未綁定,而且關(guān)注第一個(gè)參數(shù)。一個(gè)靜態(tài)方法可以有任意的簽名;它可以沒有參數(shù),而第一個(gè)參數(shù)沒有什么作用。你可以認(rèn)為靜態(tài)方法就是一個(gè)普通的函數(shù),你可以正常的調(diào)用,盡管它事實(shí)上是一個(gè)綁定到類的方法。當(dāng)它并不是必須要定義靜態(tài)方法時(shí)(你總可以定義一個(gè)正常的函數(shù)取代之),一些程序員認(rèn)為它們是一個(gè)精致的選擇當(dāng)一個(gè)函數(shù)的目的是更緊的綁定到一些特殊類上。

            要?jiǎng)?chuàng)建一個(gè)靜態(tài)方法,調(diào)用內(nèi)置類型 ``staticmethod`` 然后把它的結(jié)果綁定到一個(gè)類屬性。像所有的屬性綁定,通常是在類體中被完成的,當(dāng)然你也可以放在其他位置。唯一要給 ``staticmethod`` 的參數(shù)就是當(dāng)python調(diào)用這個(gè)靜態(tài)方法時(shí)要調(diào)用的函數(shù)。下面的例子展示了如何定義和調(diào)用一個(gè)靜態(tài)方法:

            ::

                class AClass(object):
                    def astatic():
                        print 'a static method'
                    astatic=staticmethod(astatic)

                anInstance=AClass()
                AClass.astatic()
                #prints: a static method
                anInstance.astatic()
                #prints: a static method

            這個(gè)例子對(duì)于要傳遞給靜態(tài)方法的函數(shù)和靜態(tài)方法返回的結(jié)果使用相同的名字。這個(gè)方式并不是強(qiáng)制的,但是個(gè)好的主意,而且我也推薦使用。在Python 2.4中提供了特殊,簡單的語法,可以參考 **裝飾符** 。

            5.1.10.2 Class methods
            ~~~~~~~~~~~~~~~~~~~~~~

            一個(gè)類方法是一個(gè)方法,你可以在類或其任意實(shí)例中調(diào)用。Python把你所調(diào)用方法的類綁定給了這個(gè)方法的第一個(gè)參數(shù),或者也可以所調(diào)用方法的實(shí)例的類;它并不會(huì)像綁定方法一樣,把實(shí)例綁定給第一個(gè)參數(shù)。對(duì)于類方法來說,是沒有等同的未綁定方法的。第一個(gè)參數(shù)一般約定為 ``cls`` 。雖然定義一個(gè)類方法不是必須的(你可以定義一個(gè)函數(shù),然后接受類對(duì)象作為第一個(gè)參數(shù)),一些程序員認(rèn)為對(duì)于這些函數(shù)來說是不錯(cuò)的選擇。

            要?jiǎng)?chuàng)建一個(gè)類方法,調(diào)用內(nèi)置類型 ``classmethod`` 然后再把結(jié)果綁定到類屬性上。也許所有的屬性綁定,經(jīng)常將其寫在類體中,但你也可以在其他地方實(shí)現(xiàn)。而唯一的參數(shù)就是這個(gè)要作為類方法調(diào)用的函數(shù)。下面是定義和調(diào)用:

            ::

                class ABase(object):
                    def aclassmet(cls):
                        print 'a class method for',cls.__name__
                    aclassmet=classmethod(aclassmet)

                class ADeriv(ABase):
                    pass

                bInstance=ABase()
                dInstance=ADeriv()
                ABase.aclassmet()
                #prints: a class method for ABase
                bInstance.aclassmet()
                #prints: a class method for ABase
                ADeriv.aclassmet()
                #prints: a class method for ADeriv
                dInstance.aclassmet()
                #prints: a class method for ADeriv

            同樣,和上一小節(jié)所說,可以使用 **裝飾符** 。

            5.1.11 Properties
            -----------------

            python提供一個(gè)內(nèi)置的 **overriding** 描述符,你可以用來給出一個(gè)類的實(shí)例 **property** 。

            一個(gè) **property** 是一個(gè)實(shí)例的屬性,有著特殊的功能。你引用,綁定,解綁定一個(gè)屬性可以用一個(gè)普通的語法(也就是 ``print x.prop`` ,``x.prop=23`` , ``del x.prop`` )。但是,不僅僅是這些普通的語義,你還可以給其指定特殊的方法。下面是定義一個(gè)只讀的屬性:

            ::

                class Rectangle(object):
                    def __init__(self,width,height):
                        self.width=width
                        self.height=height
                    def getArea(self):
                        return self.width*self.height
                    area=property(getArea,doc='area of the rectangle')

            每一個(gè)類 ``Rectangle`` 的實(shí)例 ``r`` 都有個(gè)只讀的屬性 ``r.area`` ,通過調(diào)用方法 ``r.getArea()`` 把矩形的兩邊相乘。而docstring ``Rectangle.area.__doc__`` 則是 ```area of the rectangle``` 。屬性 ``r.area`` 是只讀的(嘗試重綁定或解綁定都會(huì)失敗)因?yàn)槲覀冎唤o ``property`` 一個(gè) ``get`` 方法,而沒有 ``set`` 或 ``del`` 方法。

            屬性和特殊方法 ``__getattr__`` , ``__setattr__`` , ``__delattr__`` 的工作差不多,但是方法更快更簡單。你可以創(chuàng)建一個(gè)屬性通過調(diào)用內(nèi)置的 ``property`` 并把結(jié)果綁定給類屬性。想其他的屬性,一般是放在類體中,當(dāng)然你也可以放其他地方。在類 ``C`` 內(nèi),使用這樣的語法:

            ::

                attrib=property(fget=None,
                fset=None, fdel=None,
                doc=None)

            當(dāng) ``x`` 是 ``C`` 的實(shí)例,你又引用 ``x.attrib`` ,python會(huì)調(diào)用 ``fget`` 給屬性構(gòu)造器,但沒有參數(shù)。當(dāng)以 ``x.attrib=value`` 賦值是,python會(huì)調(diào)用 ``fset`` ,并把 ``value`` 作為唯一傳人的參數(shù)。而當(dāng)執(zhí)行 ``del x.attrib`` ,python又會(huì)執(zhí)行 ``fdel`` ,不傳人任何參數(shù)。python又會(huì)將 ``doc`` 的內(nèi)容作為 ``doctstring`` 。 ``property`` 的全部參數(shù)都是 *可選的* 。當(dāng)一個(gè)參數(shù)沒有,那么這個(gè)操作就被認(rèn)為是禁止的(此時(shí),python會(huì)產(chǎn)生一個(gè)異常)。舉個(gè)例子,剛才我們使 ``Rectangle`` 的屬性 ``area`` 只讀,那是因?yàn)槲覀冎惶峁┝?``fget`` ,而沒有 ``fset`` 或 ``fdel`` 。

            5.1.11.1 Why properties are important
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            屬性的重要性在于,它們的存在使得非常安全,而且你可以使這個(gè)屬性作為公共接口。如果這變得必要的,在你以后的版本中可能需要這個(gè)屬性多態(tài)運(yùn)行,或是在被引用、重綁定或解綁定時(shí)調(diào)用其他的一些代碼,你知道你將會(huì)將簡單的屬性變成 **property** ,而且得到想要的效果但不對(duì)其他代碼產(chǎn)生影響。這就使你避免笨的特色,像一個(gè) *accessor* 或 *mutator* 方法,這是在缺少 **property** 或等同機(jī)制的OOp中經(jīng)常需要的。舉個(gè)例子,客戶代碼可以使用下面自然的語法:

            ::

                someInstance.widgetCounter += 1

            而不是像下面這種嵌套的形式:

            ::

                someInstance.setWidgetCounter(someInstance.getWidgetCounter()+1)

            如果編寫代碼時(shí),你考慮使用 ``getThis`` 或 ``setThis`` 的名字,那么就考慮使用 **property** 來變得清楚。

            5.1.11.2 Properties and inheritance
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            **property** 也可以像其他屬性那樣繼承。但是,這里可能有點(diǎn)小陷阱: **property** 所調(diào)用的是它所在類中的方法,而不是其子類的方法。舉個(gè)例子:

            ::

                class B(object):
                    def f(self):
                        return 23
                    g=property(f)

                class C(B):
                    def f(self):
                        return 42

                c=C()
                print c.g
                #prints 23, not 42

            屬性 ``c.g`` 調(diào)用了 ``B.f`` ,而不是你直覺上所想的 ``C.f`` 。這個(gè)原因很簡單:因?yàn)?**property** 是通過傳遞函數(shù)對(duì)象 ``f`` 創(chuàng)建的(而且是在 ``B`` 被執(zhí)行時(shí)所創(chuàng)建,所以這個(gè)時(shí)候,這個(gè)函數(shù)對(duì)象就被認(rèn)為是 ``B.f`` )。事實(shí)上, ``f`` 在子類 ``C`` 中被重新定義了,但是因?yàn)?**property** 并沒有搜尋到它,而只是使用了先前的那個(gè)。如果你需要解決這個(gè)問題,那么就需要引入一個(gè)間接層:

            ::

                class B(object):
                    def f(self):
                        return 23
                    def _f_getter(self):
                        return self.f()
                    g=property(_f_getter)

                class C(B):
                    def f(self):
                        return 42

                c=C()
                print c.g
                #prints 42, as expected

            這里,被 **property** 所持有的函數(shù)對(duì)象是 ``B._f_getter`` ,它在執(zhí)行時(shí)會(huì)搜尋到 ``f`` (因?yàn)樗{(diào)用了 ``self.f()`` );所以,覆寫的 ``f`` 會(huì)起效果。

            5.1.12 __slots__
            ----------------

            一般來說,任何類 ``C`` 的每個(gè)實(shí)例對(duì)象 ``x`` 都有一個(gè)字典 ``x.__dict__`` ,這是python用來讓你任意綁定屬性給 ``x`` 的。為了節(jié)省點(diǎn)內(nèi)存(讓 ``x`` 僅使用預(yù)定義的屬性),你可以定義一個(gè)新式類 ``C`` 的 ``__slots__`` 屬性,這是一個(gè)字符串(通常是表示符)序列(通常是元組)。當(dāng)一個(gè)新式類 ``C`` 有個(gè)屬性 ``__slots__`` 時(shí),一個(gè)類 ``C`` 的直接實(shí)例 ``x`` 是沒有 ``x.__dict__`` 的,而且嘗試綁定不在 ``C.__slots__`` 中的屬性將會(huì)產(chǎn)生異常。使用了 ``__slots__`` 就使你減少了內(nèi)存的消耗,對(duì)于小的實(shí)例對(duì)象,這是很值得的,當(dāng)有很多這樣的對(duì)象時(shí),就節(jié)省了大大的空間。但不像大多數(shù)的屬性, ``__slots__`` 只有當(dāng)一些類體中的語句把它綁定為類屬性時(shí)才有效。后來修改 ``__slots__`` 是不起效果的,繼承也是。這里是把 ``__slots__`` 增加到 ``Rectangle`` 中:

            ::

                class OptimizedRectangle(Rectangle):
                    __slots__='width','height'

            我們不需要給 ``area`` 定義個(gè) *slot* 。 ``__slots__`` 中并不放 **property** ,僅僅是普通的實(shí)例對(duì)象,這些屬性如果沒有在 ``__slots__`` 中定義,將會(huì)搜尋 ``__dict__`` 。

            5.1.13 __getattribute__
            -----------------------

            對(duì)于新式的實(shí)例,實(shí)例屬性的引用都是通過特殊方法 ``__getattribute__`` 執(zhí)行的。這個(gè)方法是基類 ``object`` 所提供的,它執(zhí)行了所有具體的屬性引用。但是,你也可以覆寫這個(gè) ``__getattribute__`` 方法,像隱藏被繼承類的屬性。下面的例子展示了在新式對(duì)象模型中一個(gè)沒有 ``append`` 的鏈表:

            ::

                class listNoAppend(list):
                    def __getattribute__(self,name):
                        if name=='append':
                            raise AttributeError,name
                        return list.__getattribute__(self,name)

            類 ``listNoAppend`` 的實(shí)例 ``x`` 幾乎和內(nèi)置的鏈表對(duì)象是一樣的,除了當(dāng)調(diào)用 ``x.append`` 時(shí)將產(chǎn)生異常。

            5.1.14 Per-Instance Methods
            ---------------------------

            不管舊式或是新式的對(duì)象模型,都允許一個(gè)實(shí)例,擁有一個(gè)實(shí)例特化的屬性綁定,包括可調(diào)用的屬性。對(duì)于一個(gè)方法,就像其他的屬性(除了那些新式類中的 **overriding** 描述符),一個(gè)實(shí)例特化綁定隱藏了一個(gè)類級(jí)的綁定:屬性搜索如果在實(shí)例中找到了就不會(huì)考慮類了。在兩種對(duì)象模型中,對(duì)于可調(diào)用屬性的一個(gè)實(shí)例特化的綁定不會(huì)進(jìn)行什么變換。換句話說,一個(gè)屬性引用返回相同的可調(diào)用對(duì)象,而且已經(jīng)在先前已經(jīng)綁定。

            舊式和新式的對(duì)象模型的確有區(qū)別,在每個(gè)實(shí)例綁定的效果上,python會(huì)隱式地調(diào)用。在經(jīng)典的的對(duì)象模型中,一個(gè)實(shí)例一般會(huì)覆寫一個(gè)特殊方法,然后python在隱式調(diào)用方法時(shí)使用每個(gè)實(shí)例的綁定。在新式的對(duì)象模型上,特殊方法的隱式使用一般依賴于特殊方法的類級(jí)綁定,如果有的話。下面的代碼展示了兩者的區(qū)別:

            ::

                def fakeGetItem(idx):
                    return idx

                class Classic:
                    pass

                c=Classic()
                c.__getitem__=fakeGetItem
                print c[23]
                # prints: 23

                class NewStyle(object):
                    pass

                n=NewStyle()
                n.__getitem__=fakeGetItem
                print n[23]
                #prints in:
                # Traceback (most recent call last):
                #   File "<stdin>", line 1, in ?
                # TypeError: unindexable object

            經(jīng)典對(duì)象模型的語義在這個(gè)方面可能比較有用。但是,新式對(duì)象模型的方法更普遍,而且它調(diào)整和簡化了類和元類的關(guān)系。

            5.1.15 Inheritance from Built-in Types
            --------------------------------------

            一個(gè)新式類可以從內(nèi)置類型派生。但是,一個(gè)類可能直接或間接從多個(gè)內(nèi)置的類型派生,如果這些類型是特殊設(shè)計(jì)的,來允許這個(gè)層次的兼容。python不支持不受拘束的繼承從多個(gè)任意的內(nèi)置對(duì)象中。一般的,一個(gè)新式類只能以其中一種派生,當(dāng)然也包括 ``object`` ,這是所有的類型的超類。舉個(gè)例子:

            ::

                class noway(dict,list):
                    pass

            將會(huì)產(chǎn)生一個(gè) ``TypeError`` 異常,具體會(huì)解釋為“當(dāng)調(diào)用元類的基類有錯(cuò)誤:多基類有實(shí)例的沖突”。如果你曾經(jīng)看過這樣的錯(cuò)誤,這就意味著你嘗試?yán)^承,直接或間接,從多個(gè)內(nèi)置類型派生,但他們并不是設(shè)定為可合作的。

            posted on 2011-02-03 10:55 mirguest 閱讀(334) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Python

            久久精品国产99久久无毒不卡| 理论片午午伦夜理片久久| 一本色道久久88综合日韩精品| 久久久无码精品亚洲日韩京东传媒 | 国产精品久久毛片完整版| 久久久精品久久久久特色影视| 中文字幕无码免费久久| 久久本道久久综合伊人| 2021精品国产综合久久| 久久不见久久见免费影院www日本| 亚洲精品无码久久不卡| 91精品国产高清91久久久久久 | 亚洲精品无码久久久| 国产午夜精品理论片久久影视 | 久久精品国产AV一区二区三区| 99久久er这里只有精品18| 无码伊人66久久大杳蕉网站谷歌| 久久精品国产亚洲AV久| 品成人欧美大片久久国产欧美...| 免费观看久久精彩视频| 超级碰久久免费公开视频| 日韩精品无码久久久久久| 欧美日韩精品久久免费| 亚洲中文字幕无码久久2017| 久久99精品久久久久久齐齐| 国产精品久久久久影院色| 伊人久久大香线蕉AV色婷婷色| 久久免费99精品国产自在现线| 亚洲一区二区三区日本久久九| 久久久久久毛片免费看| 欧美精品一本久久男人的天堂 | 久久露脸国产精品| 国产呻吟久久久久久久92| 青青青青久久精品国产| 777久久精品一区二区三区无码 | 久久99精品国产麻豆不卡| 国产成人精品久久一区二区三区av| 精品久久久久久亚洲| 99久久99久久精品国产片果冻| 久久综合久久久| 久久精品国产亚洲Aⅴ香蕉|