随笔 - 41, 文章 - 8, 评论 - 8, 引用 - 0
数据加载中……

python in nutshell Section 5.1

本来用sphinx+rst输出html,但贴上来发现效果不行,因此直接贴上rst的。

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

如果你已经熟悉面向对象编程,像在其他语言,C++或Java,这样你可能有一个很好的理解,像类和实例:一个类,就是一个用户定义的类型,你可以将它实例化得到实例对象,而它们就是这个类型的。Python支持这些概念,通过它的类和各个实例对象。

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

类就是有一些特性的python对象:

* 你可以调用一个类,好像它就是一个函数。这个调用返回了另一个对象,这就是一个类的实例;而类也就是这个实例的类型。
* 一个类的属性可以任意的绑定和引用。
* 类属性的值可以是描述符(包括函数),或者是普通的数据对象。
* 类属性绑定了函数,就是我们所知的类的方法。
* 一个方法,可以有一个特殊的python定义的名字,它是以两个下划线起始,而又以两个下划线结尾。Python会隐式地调用这些特殊的方法,如果一个类提供了它们,并且当各个操作发生在那个类的实例上。
* 一个类可以从其他类中继承,这就意味着它如果在本身中没有找到一个属性,那么它就会委托给其他类,让它们在它们的类中搜索。

一个类的实例,是一个python对象,有任意命名了的属性,而你可以绑定和引用。一个实例对象隐式地将在实例本身中未搜寻到的属性委托给它的类来搜索。而这个类,如果可以,又会依次的搜索它所继承的类。

在python中,类是对象,而且可以像其他对象那样使用。因此,你可以将一个类作为参数,传递给一个函数。同样的,一个函数可以返回一个类。一个类,就像其他的对象,可以绑定一个变量(全局或局部),一个容器中的元素,或者是一个对象的属性。类也可以成为一个字典的键值。事实上,类是一个普通的对象,这也就是常说的类是一级对象。

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

``class`` 语句是最常见的方式,来创建一个类对象。 ``class`` 是一个单子句符合语句,遵循下列的语法:

::

    class classname(base-classes):

        statement(s)

``classname`` 是一个标识符。它是一个变量,可以绑定到类对象,在 ``class`` 语句完成执行后。

``base-classes`` 是一系列以逗号分隔,而值都必须是类对象的表达式。这些类在不同的编程语言中有着不同的名字;这完全看你喜欢,可以是基类,超类,或者是被创建类的父类。而被创建的类,可以说是继承,派生,扩展,或是子类。这都完全看你所熟悉的语言是怎么称呼它们的。这个类也可以说是一个直接子类,或者是它基类的后代。

语法上, ``base-classes`` 是可选的:为了表示你创建的类是没有基类的,你可以忽略 ``base-classes`` (而且还有它两边的括号也要忽略),直接将冒号放在类名字后面(在python 2.5中,你也可以使用这对空括号,其意思是一样的)。但是,一个没有基类的类,由于向后兼容的问题,所以是一个 **旧式** 的类(除非你定义了 ``__metaclass__`` 属性)。为了创建 **新式** 的类,而又没有“真正”的基类,那么这样编写代码: ``class C(object):`` ;因为每个类型都把内置对象归入子类,为了区分是新式的类还是旧式的类,我们就用 ``object`` 来加以指明。如果你的类的祖先都是旧式的类,而又没有定义 ``__metaclass__`` 属性,那么它就是旧式的;否则,有基类的类总是新式的类(就算有些基类是新式的,有些是旧式的)。

在各个类中的子类关系,是可传递的:如果 ``C1`` 是 ``C2`` 的子类,而 ``C2`` 又是 ``C3`` 的子类,那么, ``C1`` 就是 ``C3`` 的子类。内置函数 ``issubclass(C1,C2)`` 接受两个是类对象的参数:它将会返回 ``True`` 如果 ``C1`` 是 ``C2`` 的子类;否则将返回 ``False`` 。任何一个类将被认为是自己的子类;所以, ``issubclass(C,C)`` 将会返回 ``True`` 。关于基类如何影响这个类的方式可以参考“继承”。

而在 ``class`` 语句后面的不为空的语句,则被称为类体。一个类体在 ``class`` 语句被执行时也就被作为其一部分被执行了。直到类体结束执行,一个新的类对象还不存在,并且 ``classname`` 标识符也并未绑定(或重绑定)。在“如何用元类构造类中”提供了更详细的信息。

最后,注意, ``class`` 语句并不是立即创建任何新类的实例,而是定义了一系列的属性的集合,而这个集合又被你后来创建的实例所共享。

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

一个类的主体,是你指定类属性的地方;这些属性可以是描述符对象(包括函数),或者是任意类型的普通数据对象(一个类的属性可以是另一个类,举个例子,你可以有一个嵌套的 ``class`` 语句在另一个 ``class`` 语句中。)

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

你可以指定一个类的属性,通过绑定一个值给类体中的标识符。举个例子:

::

    class C1(object):
        x = 23

    print C1.x
    #prints:23

类对象 ``C1`` 有一个属性,叫做 ``x`` ,它绑定了一个值 ``23`` ,而 ``C1.x`` 则指向这个属性。

你可以在类体外绑定或解除属性。举个例子:

::

    class C2(object):
        pass

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

但是,你的程序如果把绑定属性之类的东西都放入类体中,这样可读性可能高一点。任何类属性,都是隐式的被全部的实例共享,一旦实例被创建。这个我们后面很快就会讨论。

使用了 ``class`` 语句后,将会隐式的设置一些类属性。属性 ``__name__`` 是 ``classname`` 标识符的字符串值。而属性 ``__bases__`` 则是一个tuple,里面是各个给定的基类对象。举个例子,使用刚才创建的 ``C1`` :

::

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

一个类还有个属性 ``__dict__`` ,它是一个字典对象,类用它来存放所有其他的属性。对于任意类对象 ``C`` ,任意对象 ``x`` ,和任意标识符 ``s`` (除了 ``__name__`` , ``__bases__`` 和 ``__dict__`` ), ``c.s`` 是和 ``c.__dict__['s']=x`` 等价的。举个例子,再次引用刚才创建的 ``C1`` :

::

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

以下的设定是没有区别的:像在类体内定义属性,或是在类体外通过指定一个属性,再或者,在类体外,显式地绑定到 ``c.__dict__`` 。

直接在类体中的语句,如果引用属性时,要使用简单的名字,而不是完全的名字。举个例子:

::

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

但是呢,如果是在类体中所定义的方法内,引用时就必须使用完整的名字,而不是简单的名字。举个例子:

::

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

注意,使用属性引用时(也就是像 ``c.s`` )比函数绑定有更多的语义。具体参考“Attribute Reference Basics”。

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

大多数类体内包含 ``def`` 语句,因为函数(在此处被称为方法)对于很多类对象来说都是很重要的属性。一个 ``def`` 语句尽管在类体中,但是仍然是和 “Function”的约定是一样的。另外,在类体中的方法一直要有个强制的第一个参数,约定称为 ``self`` ,而他表示你调用方法所对应的实例。在方法调用中, ``self`` 扮演一个很重要的角色。

这里是一个例子,一个类包含一个方法:

::

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

一个类可以定义多个特殊方法(就是方法的名字前后都有两个下划线),而它们又与实例的特殊操作有关。我在“Special Methods”中有详细讨论。

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

当一个类体中的语句(或者是一个方法)使用一个标识符以两个下划线开头(但不是结尾),比如就像 ``__ident`` ,python就会隐式地将标识符转成了 ``_classname__ident`` ,此次 ``classname`` 就是类的名字。这就可以使一个类使用私有的命名,以此减少无意中命名冲突的风险。

根据约定,所有以单下划线起始的标识符,意味着对于绑定它们的作用域是私有的,不管这个作用域是不是一个类。python编译器并不会强制进行转换;这完全取决于编程人员来遵守它。

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

如果在类体中的第一条语句是一个字符串常量,那么编译器会将其认为是这个类的文档。这个属性叫做 ``__doc__`` 而且也被叫做一个类的 *docstring* 。更多地参考 “Docstring”。

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

一个描述符,就是一个新式的对象,而这个类提供了特殊的方法叫做 ``__get__`` 。描述符就是在语义上,控制一个实例属性的访问及设定。通俗的来讲,就是当你访问一个实例的属性时,python会通过调用描述符的 ``__get__`` 来获取属性值。具体参考“Attribute Reference Basics”。

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

如果一个描述符类还提供了特殊方法 ``__set__`` ,那么这个描述符就是所谓的 *overriding descripter* (或者,一个旧式的,比较混乱的术语, *data descriptor* );如果这个描述符类仅仅提供了 ``__get__`` ,而没有 ``__set__`` ,那么这个描述符就是所谓的 *nonoverriding descriptor* (或者是 *nondata* )。举个例子,一个函数对象的类如果提供了 ``__get__`` ,但没有 ``__set__`` ;那么,这个函数对象就是个 *nonoverriding descriptor* 。通俗地讲,当你指派一个值给一个实例的属性,而它又是一个 *overriding* 的描述符,那么python会调用 ``__set__`` 来赋值。
具体的参考“Attributes of instance object”。

旧式的类也可以有描述符,但是在旧式类中的描述符运行起来就好像是 *nonoverriding* 一样(它们的 ``__set__`` 方法,就会被忽略)。

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

为创建一个类的实例,可以像调用函数一样,调用那个类对象。每次调用都会返回一个新的实例,而它的类型就是那个类:

::

    anInstance=C5()

你可以调用内置函数 ``isinstance(I,C)`` ,用一个类对象 ``C`` 作为参数。如果 ``isinstance`` 返回 ``True`` ,那么标识实例 ``I`` 是类 ``C`` 的实例或是其子类。不然, ``isinstance`` 返回 ``False`` 。

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

当一个类定义了或继承了一个名为 ``__init__`` 的方法,那么调用这个类对象时,就会隐式地执行 ``__init__`` 来初始化一个新的实例。而调用时传入的参数必须要和 ``__init__`` 的参数一致,除了第一个参数 ``self`` 。举个例子,考虑下面的类:

::

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

这里是如何创建 ``C6`` 的实例:

::

    anotherInstance = C6(42)

就像在 ``C6`` 中展示的, ``__init__`` 方法一般含有绑定实例属性的语句。而且,一个 ``__init__`` 方法不能够返回一个值;否则,python会抛出一个 ``TypeError`` 异常。

方法 ``__init__`` 的目的,是为了绑定,从而创建新建实例的属性。你也可以绑定或解除实例的属性在 ``__init__`` 的外面,就像你等等会看到的。但是,你的代码会有更好的可读性,如果你在 ``__init__`` 方法中初始化所有属性的值。

当 ``__init__`` 是缺省的,那么你调用时不能给定参数。而且,新生成的实例没有因实例而特殊的属性。

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

一旦你创建了一个实例,那么你就可以访问这个属性(数据和方法)通过使用点( ``.`` )操作符。举个例子:

::

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

属性引用有很丰富的语义,具体参考“Attribute Reference Basics”。

你可以给一个实例对象任意的属性,通过绑定一个值给一个属性引用。举个例子:

::

    class C7:
        pass

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

实例对象 ``z`` 现在就有一个名为 ``x`` 的属性,绑定了值 ``23`` ,而 ``z.x`` 则指向那个属性。注意特殊方法 ``__setattr__`` ,如果存在时,将会侦听每个绑定属性的行为。此外,对于一个新式的实例,如果你尝试绑定属性,而这个属性又是一个 *overriding* 描述符,那么这个描述符的 ``__set__`` 将会监听。在这种情况下,语句 ``z.x=23`` 其实是执行了 ``type(z).x.__set__(z,23)`` (旧式的实例将会忽略描述符的这个特性,也就是说,它们从未调用它们的 ``__set__`` 方法)。

创建一个实例时将隐式地设置两个实例属性。对于任何实例 ``z`` , ``z.__class__`` 是 ``z`` 所属的类对象,而 ``z.__dict__`` 则是一个字典用来存放它的其它属性。例如,对于实例 ``z`` 我们这样创建:

::

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

你可能重新绑定(但不是解除)任意或全部的属性,但是这很少是必需的。一个新式实例的 ``__class__`` 仅仅可能被重绑定到新式类上,而且一个 *legacy* 实例的 ``__class__`` 仅能绑定到 *legacy* 的类上。

对于任何实例 ``z`` ,对象 ``x`` ,和标识符 ``s`` (除了 ``__class__`` 和 ``__dict__`` ), ``z.s=x`` 等价于 ``z.__dict__['s']=x`` (除非有个特殊方法 ``__setattr__`` ,或者是一个 *overriding* 描述符的特殊方法 ``__set__`` ,将会监听尝试绑定的行为)。举个例子,重新使用刚才创建的 ``z`` :

::

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

在实例创建属性时使用 ``__init__`` 和显示使用 ``z.__dict__`` 是一样的。

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

一个常见的任务是根据一些条件来创建不同类的实例,或者是防止创建一个新实例,假如已经存在一个可复用的。一个常见的误解就是,在 ``__init__`` 应该返回一个对象,但是这是很难实现的:python会产生异常,当返回了任何值而不是 ``None`` 。最好的方式来执行一个灵活对象的创建,是使用普通的函数,而不是直接调用这个类对象。一个扮演这个角色的函数称为一个 *factory function* 。

调用一个 *factory function* 是一个很灵活的方法:一个函数可能会返回一个已存在可复用的实例,或者是创建一个新的实例通过调用任何适合的类。加入你有两个差不多可交换的类( ``SpecialCase`` 和 ``NormalCase`` ),而你希望依据参数,灵活地创建合适的实例。下面的 *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__
~~~~~~~~~~~~~~~

每一个新式类都有(或继承了)一个静态方法( **static method** ) ``__new__`` 。当你调用 ``C(*args,**kwds)`` 来创建类 ``C`` 的新实例时,Python会首先调用 ``C.__new__(C,*args,**kwds)`` 。Python使用 ``__new__`` 的返回值 ``x`` 作为新创建的实例。然后,python会再调用 ``C.__init__(x,*args,**kwds)`` ,但是仅仅当 ``x`` 确实是 ``C`` 或它子类的实例时(不然,``x`` 的状态仍处于 ``__new__`` 留给它的 )。因此,举个例子,语句 ``x=C(23)`` 等同于:

::

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

这里, ``object.__new__`` 将创建一个新的,为初始化的实例,并且接受这个类作为第一个参数。它将会忽略其他的参数,如果它有一个 ``__init__`` 方法。但是如果它接受其他参数在这个参数之前,那么将会产生一个异常。当然如果没有 ``__init__`` 方法也是会抛出异常的。当你在类体中重载了 ``__new__`` ,你不需要增加 ``__new__=staticmethod(__new__)`` ,就如你一般会喜欢的:python会识别这个名字 ``__name__`` 并且在它的上下文特殊地处理它。在那些不常有的情况下,如果你在类体外面重新绑定了 ``C.__new__`` 那么你就确实需要使用 ``C.__new__=staticmethod(whatever)`` 。

对于 ``__new__`` 有着大部分 **factory function** 的灵活性。 ``__new__`` 将会适时地选择返回一个已存在的实例或创建一个新的实例。当 ``__new__`` 确实需要创建一个新的实例,它经常会把创建的任务委托给 ``object.__new__`` 的调用或者是它其他父类的 ``__new__`` 的调用。下面的例子演示了如何重载静态方法 ``__new__`` 为了实现一个 **Singleton** 设计模式:

::

    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`` 的子类(当然没有重载 ``__new__`` ),就将只有一个实例。如果一个子类定义了一个 ``__init__`` 方法,这个子类必须确保它的 ``__init__`` 是安全的,当被重复调用时(在每次的创建请求下)。

旧式的类是没有 ``__new__`` 方法的。

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

一个属性引用就是形如 ``x.name`` 的表达式,这里 ``x`` 可以是任何表达式,而 ``name`` 是一个被称为 *attribute name* 的标识符。很多种python对象都具有属性,但是一个熟悉引用在 ``x`` 指向一个类或实例时具有特殊的丰富的语义。记住,方法也是属性,所以任何我们所谓的属性同样也适用于可调用的属性(比如说方法)。

假如 ``x`` 是一个类 ``C`` 的实例,而它又是继承自基类 ``B`` 。两个类和实例有一些属性(数据和方法),就像下面的:

::

    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

一些属性名字是特殊的。举个例子, ``C.__name__`` 是字符串 ``'C'`` 而且是这个类名称。 ``C.__bases__`` 则是一个元组 ``(B,)`` ,这是类 ``C`` 的基类。 ``x.__class__`` 是类 ``C`` ,它是 ``x`` 所属的类。当你指向这些特殊的属性,这个属性引用将会访问给定的位置,然后把它找到的值拿出来。你不能解除这些属性。重新绑定是允许的,所以你可以改变名字或者是这个类的基类,或者是实例的类对象,匆忙地,但是这个高级的技术非同寻常的重要。

类 ``C`` 和实例 ``x`` 都各自有一个其他的特殊属性:一个叫做 ``__dict__`` 的字典。全部其他的属性,除了仅有的几个特殊属性外,全部都被放在类或实例的 ``__dict__`` 中。

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

当你使用语法 ``C.name`` 来指向一个类 ``C`` 上的属性,搜索会分成两步进行:

1. 如果 ``name`` 就在 ``C.__dict__`` 中,那么 ``C.name`` 将会从 ``C.__dict__['name']`` 中取得值 ``v`` 。然后,如果 ``v`` 是一个描述符(也就是说, ``type(v)`` 提供了名为 ``__get__`` 的方法), ``C.name`` 的值将是调用 ``type(v).__get__(v,None,C)`` 的结果。否则, ``C.name`` 的值就是 ``v`` 。
2. 否则, ``C.name`` 就委托给 ``C`` 的基类来查找,意味着它会沿着 ``C`` 的祖先一个个查找 ``name`` (关于其查找顺序,参考1.8.1)。

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

但你使用 ``x.name`` 来指向类 ``C`` 的实例 ``x`` ,搜索会分成三步进行:

1. 当在 ``C`` (或者是在 ``C`` 的祖先类中)找到了 ``name`` ,作为一个 *overriding* 描述符 ``v`` 的名字(也就是说, ``type(v)`` 提供了 ``__get__`` 和 ``__set__`` 方法), ``C.name`` 的值将是调用 ``type(v).__get__(v,x,C)`` 的结果。(此步不适用于旧式实例)。
2. 否则,当 ``name`` 是在 ``x.__dict__`` 中的键值,那么 ``x.name`` 将会获取并返回 ``x.__dict__['name']`` 的值。
3. 否则, ``x.name`` 将搜索委托给 ``x`` 的类(和搜索 ``C.name`` 的两步是一样的,就像刚才所说的 )。如果一个描述符 ``v`` 被找到了,那么属性搜寻的结果,又将是 ``type(v).__get__(v,x,C)`` ;如果是一个非描述符值 ``v`` 被找到,那么结果就是 ``v`` 。

当这些搜索步骤结束后没有找到属性,python就会产生一个 ``AttributeError`` 的异常。但是,对于 ``x.name`` 的搜索,如果 ``C`` 定义或者继承了特殊方法 ``__getattr__`` ,python将会调用 ``C.__getattr__(x,'name')`` 而不是产生一个异常(这完全取决于 ``__getattr__`` 是返回一个合适的值还是产生一个合适的异常,经常是 ``AttributeError`` )。

考虑下面的属性引用:

::

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

这里的 ``x.e`` 和 ``x.d`` 在实例搜寻的第二步就成功了,因为没有涉及描述符,而且 ``e`` 和 ``d`` 都是在 ``x.__dict__`` 中的键值。所以,搜索不会继续,而是返回 ``88`` 和 ``77`` 。而其他三个则是到第三步,并且搜索了 ``x.__class__`` (也就是 ``C`` )。 ``x.c`` 和 ``x.b`` 是在类搜寻的第一步就成功了,因为 ``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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

注意属性搜寻的步骤,只有在你引用它时才会发生,而不是在你绑定一个属性的时候。当你绑定(不管是类还是实例)一个属性时,而它的名字不是特殊的(除非有个 ``__setattr__`` 方法,或者是一个 *overriding* 描述符的 ``__set__`` 方法,截取一个实例属性的绑定),你仅仅影响了在 ``__dict__`` 中的属性(在类或实例中)。换句话说,在属性绑定的情况下,是没有搜索被执行的,除了检查是否是个 *overriding* 描述符。

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

一个函数对象的 ``__get__`` 方法返回一个包装了函数的 *unbound method object* 或 *bound method object* 。在两者间的不同点在于,一个 **unbound method** 没有和一个特别的实例绑定,而 **bound method** 则有。

在前面段落的代码中,属性 ``f`` , ``g`` 和 ``h`` 都是函数;所以,对于任一的属性引用将会返回一个包装了相应函数的方法对象。考虑下面的:

::

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

这个语句将输出三个绑定方法,像这样:

::

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

然后是像这样的三个未绑定方法:

::

    <unbound method C.h>

当我们引用实例 ``x`` 的属性时将返回绑定方法,而引用类 ``C`` 的属性时则返回未绑定的方法。

因为一个绑定的方法已经和一个特殊的实例绑定了,你可以这样调用方法:

::

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

这里要注意的就是,你不需要传给方法第一个参数, ``self`` 。对于实例 ``x`` 的绑定方法会隐式地把对象 ``x`` 绑定给参数 ``self`` 。因此,方法的主体能够访问实例的属性,作为 ``self`` 的属性,尽管我们没有显示的传递给方法这个参数。

一个未绑定的方法,是没有和一个特殊实例关联的,所以你必须指定一个合适的对象给第一个参数,当你调用一个未绑定方法,举个例子:

::

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

你调用一个未绑定的方法比绑定方法次数要少的多。未绑定方法的主要用途是来访问 *overridden* 方法;更好的是使用 ``super`` 。

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

就像刚才我们讨论的,当一个属性引用是从类指向函数时,返回一个引用指向那个包裹了函数的未绑定方法。一个未绑定方法除了包装的函数外还有三个额外的属性: ``im_class`` 是提供方法的类对象, ``im_func`` 是被包装的函数,而 ``im_self`` 通常返回 ``None`` 。这些属性是只读的,这意味着尝试重绑定或解除绑定都会抛出异常。

你可以调用一个未绑定的方法就如同你调用了它的 ``im_func`` 函数,但是第一个参数必须是是 ``im_class`` 的实例或是后裔。换句话说,对于未绑定方法的调用,必须至少有一个参数,这个参数应该和被包装的函数的第一个参数一致(一般称为 ``self`` )。

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

当一个属性引用自一个实例,在搜索中,找到一个函数对象,而那是一个实例的类中的属性,查找会调用函数的 ``__get__`` 方法来获取属性值。这个调用,在这种情况下,创建并返回了一个绑定的方法,并包裹着那个函数。

注意当一个属性引用的查找在 ``x.__dict__`` 中找到了函数对象,那么属性引用操作不会创建一个绑定方法,因为在这种情况下函数并不是当作一个描述符,而且,函数的 ``__get__`` 是不可调用的;更准确地说,函数对象本身就是属性值。类似的,没有一个绑定的方法是为不普通的函数调用而创建的,就如内置的(与python代码对照)函数,因为它们不是描述符。

一个绑定方法,和一个未绑定方法是类似的,它们都有三个只读的属性,那是包装函数对象多出来的。像在未绑定方法中, ``im_class`` 是一个提供方法的类对象,而 ``im_func`` 是被包装的函数。但是,在一个绑定方法的对象中,属性 ``im_self`` 指向获得方法的实例 ``x`` 。

一个绑定方法使用起来像它的 ``im_func`` 函数,但是调用绑定方法不需要显式提供第一个参数(一般约定为 ``self`` )。当你调用一个绑定方法,在传递其他参数时,绑定方法把 ``im_self`` 传递给 ``im_func`` 的第一个参数。

让我们看看下面这个底层的概念上的是如何调用 ``x.name(arg)`` 。在下面的例子中:

::

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

    class C(object):
        name = f

    x = C()

这里 ``x`` 是一个类 ``C`` 的实例对象, ``name`` 是一个标识符,指明了 ``x`` 的方法( ``C`` 的属性,它是一个函数,在这里是函数 ``f`` ),而 ``arg`` 是任何的表达式。python先检查 ``name`` 是否是 ``C`` 中的属性,并且它还是一个描述符。但是事实上它并不是,因为它们的类虽然定义了 ``__get__`` 方法,但是并不是 *overriding* 的描述符,因为它们没有 ``__set__`` 方法。python接下来检查 ``name`` 是否在 ``x.__dict__`` 中。但它们也不是。所以python在类 ``C`` 中查找 ``name`` (任何东西都会起作用以同样的方式如果 ``name`` 被找到了,通过继承,在 ``C`` 的 ``__bases__`` 中)。python注意到这个属性值,也就是函数对象 ``f`` ,是一个描述符。所以,python调用 ``f.__get__(x,C)`` ,这就创建了一个绑定方法, ``im_func`` 设定为 ``f`` , ``im_class`` 被设定为 ``C`` ,而 ``im_self`` 被设为 ``x`` 。然后python调用这个绑定方法对象,以 ``arg`` 作为唯一的参数。这个绑定方法插入了 ``im_self`` (也就是 ``x`` )作为第一个参数,而 ``arg`` 成为第二个,然后调用 ``im_func`` (也就是 ``f`` )。总的效果就像是这样调用:

::

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

当一个绑定方法的函数体执行了,它没有特别的命名空间,不管是 ``self`` 或其他类。变量引用的是局部或全局的,就像其他的函数一样。变量并不会隐式的指明 ``self`` 的属性,或是任何其他类中的属性。当一个方法需要指向,绑定,或解除绑定一个 ``self`` 对象的属性,它也需要标准的属性引用的语法(比如, ``self.name`` )。隐式的作用域可能会导致使用其他用过的,而python则不是这样(因为python和其他面向对象不同),但是这使得python简洁明了,而避免了混淆。

绑定方法对象是一级对象,你可以在任何你可以使用可调用对象的地方使用它们。因为绑定方法持有一个包装函数的引用,并且指向它所执行的 ``self`` 对象,它是更有用更灵活的选择对应嵌套的来说。一个实例对象的类提供了特殊的方法 ``__call__`` 提供了另一个可行的选择。这些概念中的每一个使你绑定一些行为(代码)和状态(数据)到一个单独的可调用对象中。嵌套可能是最简单的,但有很多的限制。这里是嵌套:

::

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

绑定方法和可调用的实例更加的丰富和灵活。这里是用绑定方法实现同样的功能:

::

    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

这里是以一个可调用实例来执行(这个实例的类提供了特殊方法 ``__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)

从这些代码调用函数来看,它们是可交换的,因为它们都返回多态的可调用对象(也就是都是可用的)。在这里,嵌套是最简单的;而其他两种则是更灵活,普遍而且更强大的机制,但是在这个简单的例子中没必要使用这么强大的功能。

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

当你使用一个属性引用 ``C.name`` ,但是 ``name`` 不在 ``C.__dict__`` 中,搜寻会隐式的在 ``C.__bases__`` 以特殊的顺序执行(历史原因,称为 *method resolution order* ,或者是 **MRO** ,甚至是对于任何属性,而不仅仅是对方法)。 ``C`` 的基类会又会依次的搜寻它们的基类。搜寻机制检查直接或间接的祖先,一个又一个,以 **MRO** 进行,当 ``name`` 被找到时将会停止。

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

在一个类中,属性名字的搜寻本质上通过以 **left-to-right** , **depth-first** 的顺序访问祖先发生。但是,在多继承的存在下(这样就使继承的图表是一个非循环的图表,而不是一个树形的),这个简单的方法可能导致一些祖先类被访问了两次。在这样的情况下,这种分解的顺序被阐明,通过在搜寻的列表留下右边的那个类,这将会持续到搜索完,然后再去刚才剩下的那些类中重新开始搜索。这个就使得多类继承很难正确有效的使用。而新式对象则在此方面更加高级。

这在 **left-right** , **depth-first** 搜索中个问题,可以很简单的在旧式类中被论证:

::

    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"

在这个情况下, ``amethod`` 的搜寻将从 ``Derived`` 开始。当没有在此处找到,将会去搜索 ``Base2`` 。因为在此处也没有找到,旧式的搜寻将会继续搜索它的父类, ``Base1`` ,而在这里找到了那个属性。所以,就是类将会停止,而不在考虑 ``Base3`` ,而在这里也可以找到那个属性。而新式的 **MRO** 则解决了那个问题,通过移除最左边的 ``Base1`` ,所以将会搜索 ``Base3`` 中的 ``amethod`` 。

Figure 5-1 展示了旧式和新式的 **MRO** ,在这个菱形的继承图中。

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

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


每一个新式的类和内置类型都有一个特殊制度的类属性,叫做 ``__mro__`` ,这是一个用于方法分解的元组,以一定顺序存放类型。你只可以在类中引用 ``__mro__`` ,而不以在实例上,而且因为 ``__mro__`` 是只读的属性,你不可以重新绑定或解除绑定。具体的,你可以参考 Michele Simionato 写的 “The Python 2.3 Method Resolution Order” ,在 http://www.python.org/2.3/mro.html

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

就像我们刚看到的,一个属性的搜寻会依照 **MRO** (典型的是根据继承树)然后在找到时就会停止。而派生类会比他们的父类先被搜寻,这样子,当子类和父类都定义了相同名字的属性时,搜索就会在子类中寻到定义并在那里停止。这就是所谓的 **覆写** 了父类中的定义。考虑下面的例子:

::

    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中你可以覆写数据属性,也可以是可调用的属性(方法)。

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

当一个子类 ``C`` 覆写了它的超类 ``B`` 中的一个方法 ``f`` 时, ``C.f`` 的主体经常需要把某些操作委托给超类中的方法来执行。这个可以使用未绑定方法,像下面:

::

    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`` 。未绑定方法最常用的就是在委托超类执行中。

一个很常见的委托就是在特殊方法 ``__init__`` 中。当python创建一个实例时,基类的 ``__init__`` 方法不会自动调用,就像在其他一些面向对象的语言中一样。因此,必要时,就依靠于使用委托来完成合适的初始化。举个例子:

::

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

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

如果在类 ``Derived`` 的 ``__init__`` 中未显式调用类 ``Base`` , ``Derived`` 的实例将会丢失初始化的部分信息,所以他将缺少属性 ``anotherattribute`` 。

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

使用未绑定方法调用超类的方法,可能在多重继承上出问题,特别是菱形状的。考虑下面的定义:

::

    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)

在这个代码中,当我们调用 ``D().met()`` , ``A.met`` 将会出现两次。那我们如何确保祖先只被调用一次,而且仅仅一次?解决的办法就是,使用内置的 ``super`` 。 ``super(aclass,obj)`` ,将会返回对象 ``obj`` 的特殊超级对象。当我们搜寻一个属性(比如一个方法)在这个超级对象中,搜寻将会开始于在 ``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()

现在, ``D().met()`` 对每个类的 ``met`` 都只调用了一次。如果你形成了在使用超类时使用 ``super`` 的习惯,那么尽管可能有很复杂的结构,你的类也能很好的使用。无论这个继承结果有多简单,使用这个并没有坏处,而且,我也推荐使用新式的对象模型。

你使用未绑定方法的技术的唯一的情况,可能就是,类之间的方法有不相容的签名,但如果你真的要处理这种东西,那么使用未绑定方法的技术可能会至少有点讨厌。多重继承的合适使用可能会有限制。但是,就算OOP的大多数基本的特征,像在基类与子类间的多态将会因为签名的不一致导致有所削减。

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

继承和覆写提供了简单有效的方式来增加或修改类属性(特别是方法)但不带侵略性的(也就是说,不会修改那个定义了属性的类)通过在子类中增加或覆写属性。但是,继承并不提供来删除或隐藏基类的属性的方式但不带侵略性的。如果子类定义或覆写一个属性失败了,python就会寻找基类的定义。如果你需要执行这样的删除,那么可能包含:

* 覆写方法并在方法内产生异常。
* 避开继承,拥有这个属性在其他地方而不是在子类的 ``__dict__`` ,而且定义 ``__getattr__`` 来有选择的委托。
* 使用新式对象模型,而且覆写 ``__getattribute__`` 实现相同的效果。

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

内置的 ``object`` 类型是全部内置类型和新式类的祖先。这个 ``object`` 类型定义了一些特殊的方法,它们执行对象的默认语义。

``__new__`` ``__init__``
    你可以创建一个 ``object`` 的直接实例,通过不带参数的调用 ``object()`` 。调用将会隐式地使用 ``object.__new__`` 和 ``object.__init__`` 来创建和返回一个没有属性的实例对象(甚至没有存放属性的 ``__dict__`` )。这个实例对象就像一个“哨兵”一样有用,保证不和其他不同的对象不相同。

``__delattr__`` ``__getattribute__`` ``__setattr__``
    默认的,一个对象操作属性引用通过使用 ``object`` 的这些方法。

``__hash__`` ``__repr__`` ``__str__``
    任意对象可以传递给函数 ``hash`` 和 ``repr`` 和传给类型 ``str`` 。

一个 ``object`` 的子类会覆写这些方法或增加其他。

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

Python提供了两种内置的 **nonoverriding** 描述符类型,这就给了一个类两种不同的“类级方法(classlevel method)”。

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

一个 **static method** 是一个方法,你可以在类上或者在类的任意实例调用,但没有特殊的行为和普通方法的约束,绑定和未绑定,而且关注第一个参数。一个静态方法可以有任意的签名;它可以没有参数,而第一个参数没有什么作用。你可以认为静态方法就是一个普通的函数,你可以正常的调用,尽管它事实上是一个绑定到类的方法。当它并不是必须要定义静态方法时(你总可以定义一个正常的函数取代之),一些程序员认为它们是一个精致的选择当一个函数的目的是更紧的绑定到一些特殊类上。

要创建一个静态方法,调用内置类型 ``staticmethod`` 然后把它的结果绑定到一个类属性。像所有的属性绑定,通常是在类体中被完成的,当然你也可以放在其他位置。唯一要给 ``staticmethod`` 的参数就是当python调用这个静态方法时要调用的函数。下面的例子展示了如何定义和调用一个静态方法:

::

    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

这个例子对于要传递给静态方法的函数和静态方法返回的结果使用相同的名字。这个方式并不是强制的,但是个好的主意,而且我也推荐使用。在Python 2.4中提供了特殊,简单的语法,可以参考 **装饰符** 。

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

一个类方法是一个方法,你可以在类或其任意实例中调用。Python把你所调用方法的类绑定给了这个方法的第一个参数,或者也可以所调用方法的实例的类;它并不会像绑定方法一样,把实例绑定给第一个参数。对于类方法来说,是没有等同的未绑定方法的。第一个参数一般约定为 ``cls`` 。虽然定义一个类方法不是必须的(你可以定义一个函数,然后接受类对象作为第一个参数),一些程序员认为对于这些函数来说是不错的选择。

要创建一个类方法,调用内置类型 ``classmethod`` 然后再把结果绑定到类属性上。也许所有的属性绑定,经常将其写在类体中,但你也可以在其他地方实现。而唯一的参数就是这个要作为类方法调用的函数。下面是定义和调用:

::

    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

同样,和上一小节所说,可以使用 **装饰符** 。

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

python提供一个内置的 **overriding** 描述符,你可以用来给出一个类的实例 **property** 。

一个 **property** 是一个实例的属性,有着特殊的功能。你引用,绑定,解绑定一个属性可以用一个普通的语法(也就是 ``print x.prop`` ,``x.prop=23`` , ``del x.prop`` )。但是,不仅仅是这些普通的语义,你还可以给其指定特殊的方法。下面是定义一个只读的属性:

::

    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')

每一个类 ``Rectangle`` 的实例 ``r`` 都有个只读的属性 ``r.area`` ,通过调用方法 ``r.getArea()`` 把矩形的两边相乘。而docstring ``Rectangle.area.__doc__`` 则是 ```area of the rectangle``` 。属性 ``r.area`` 是只读的(尝试重绑定或解绑定都会失败)因为我们只给 ``property`` 一个 ``get`` 方法,而没有 ``set`` 或 ``del`` 方法。

属性和特殊方法 ``__getattr__`` , ``__setattr__`` , ``__delattr__`` 的工作差不多,但是方法更快更简单。你可以创建一个属性通过调用内置的 ``property`` 并把结果绑定给类属性。想其他的属性,一般是放在类体中,当然你也可以放其他地方。在类 ``C`` 内,使用这样的语法:

::

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

当 ``x`` 是 ``C`` 的实例,你又引用 ``x.attrib`` ,python会调用 ``fget`` 给属性构造器,但没有参数。当以 ``x.attrib=value`` 赋值是,python会调用 ``fset`` ,并把 ``value`` 作为唯一传人的参数。而当执行 ``del x.attrib`` ,python又会执行 ``fdel`` ,不传人任何参数。python又会将 ``doc`` 的内容作为 ``doctstring`` 。 ``property`` 的全部参数都是 *可选的* 。当一个参数没有,那么这个操作就被认为是禁止的(此时,python会产生一个异常)。举个例子,刚才我们使 ``Rectangle`` 的属性 ``area`` 只读,那是因为我们只提供了 ``fget`` ,而没有 ``fset`` 或 ``fdel`` 。

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

属性的重要性在于,它们的存在使得非常安全,而且你可以使这个属性作为公共接口。如果这变得必要的,在你以后的版本中可能需要这个属性多态运行,或是在被引用、重绑定或解绑定时调用其他的一些代码,你知道你将会将简单的属性变成 **property** ,而且得到想要的效果但不对其他代码产生影响。这就使你避免笨的特色,像一个 *accessor* 或 *mutator* 方法,这是在缺少 **property** 或等同机制的OOp中经常需要的。举个例子,客户代码可以使用下面自然的语法:

::

    someInstance.widgetCounter += 1

而不是像下面这种嵌套的形式:

::

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

如果编写代码时,你考虑使用 ``getThis`` 或 ``setThis`` 的名字,那么就考虑使用 **property** 来变得清楚。

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

**property** 也可以像其他属性那样继承。但是,这里可能有点小陷阱: **property** 所调用的是它所在类中的方法,而不是其子类的方法。举个例子:

::

    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`` 调用了 ``B.f`` ,而不是你直觉上所想的 ``C.f`` 。这个原因很简单:因为 **property** 是通过传递函数对象 ``f`` 创建的(而且是在 ``B`` 被执行时所创建,所以这个时候,这个函数对象就被认为是 ``B.f`` )。事实上, ``f`` 在子类 ``C`` 中被重新定义了,但是因为 **property** 并没有搜寻到它,而只是使用了先前的那个。如果你需要解决这个问题,那么就需要引入一个间接层:

::

    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** 所持有的函数对象是 ``B._f_getter`` ,它在执行时会搜寻到 ``f`` (因为它调用了 ``self.f()`` );所以,覆写的 ``f`` 会起效果。

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

一般来说,任何类 ``C`` 的每个实例对象 ``x`` 都有一个字典 ``x.__dict__`` ,这是python用来让你任意绑定属性给 ``x`` 的。为了节省点内存(让 ``x`` 仅使用预定义的属性),你可以定义一个新式类 ``C`` 的 ``__slots__`` 属性,这是一个字符串(通常是表示符)序列(通常是元组)。当一个新式类 ``C`` 有个属性 ``__slots__`` 时,一个类 ``C`` 的直接实例 ``x`` 是没有 ``x.__dict__`` 的,而且尝试绑定不在 ``C.__slots__`` 中的属性将会产生异常。使用了 ``__slots__`` 就使你减少了内存的消耗,对于小的实例对象,这是很值得的,当有很多这样的对象时,就节省了大大的空间。但不像大多数的属性, ``__slots__`` 只有当一些类体中的语句把它绑定为类属性时才有效。后来修改 ``__slots__`` 是不起效果的,继承也是。这里是把 ``__slots__`` 增加到 ``Rectangle`` 中:

::

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

我们不需要给 ``area`` 定义个 *slot* 。 ``__slots__`` 中并不放 **property** ,仅仅是普通的实例对象,这些属性如果没有在 ``__slots__`` 中定义,将会搜寻 ``__dict__`` 。

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

对于新式的实例,实例属性的引用都是通过特殊方法 ``__getattribute__`` 执行的。这个方法是基类 ``object`` 所提供的,它执行了所有具体的属性引用。但是,你也可以覆写这个 ``__getattribute__`` 方法,像隐藏被继承类的属性。下面的例子展示了在新式对象模型中一个没有 ``append`` 的链表:

::

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

类 ``listNoAppend`` 的实例 ``x`` 几乎和内置的链表对象是一样的,除了当调用 ``x.append`` 时将产生异常。

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

不管旧式或是新式的对象模型,都允许一个实例,拥有一个实例特化的属性绑定,包括可调用的属性。对于一个方法,就像其他的属性(除了那些新式类中的 **overriding** 描述符),一个实例特化绑定隐藏了一个类级的绑定:属性搜索如果在实例中找到了就不会考虑类了。在两种对象模型中,对于可调用属性的一个实例特化的绑定不会进行什么变换。换句话说,一个属性引用返回相同的可调用对象,而且已经在先前已经绑定。

旧式和新式的对象模型的确有区别,在每个实例绑定的效果上,python会隐式地调用。在经典的的对象模型中,一个实例一般会覆写一个特殊方法,然后python在隐式调用方法时使用每个实例的绑定。在新式的对象模型上,特殊方法的隐式使用一般依赖于特殊方法的类级绑定,如果有的话。下面的代码展示了两者的区别:

::

    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

经典对象模型的语义在这个方面可能比较有用。但是,新式对象模型的方法更普遍,而且它调整和简化了类和元类的关系。

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

一个新式类可以从内置类型派生。但是,一个类可能直接或间接从多个内置的类型派生,如果这些类型是特殊设计的,来允许这个层次的兼容。python不支持不受拘束的继承从多个任意的内置对象中。一般的,一个新式类只能以其中一种派生,当然也包括 ``object`` ,这是所有的类型的超类。举个例子:

::

    class noway(dict,list):
        pass

将会产生一个 ``TypeError`` 异常,具体会解释为“当调用元类的基类有错误:多基类有实例的冲突”。如果你曾经看过这样的错误,这就意味着你尝试继承,直接或间接,从多个内置类型派生,但他们并不是设定为可合作的。

posted on 2011-02-03 10:55 mirguest 阅读(310) 评论(0)  编辑 收藏 引用 所属分类: Python


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理