Differences between revisions 1 and 13 (spanning 12 versions)
Revision 1 as of 2006-01-09 06:12:38
Size: 17431
Editor: ZoomQuiet
Comment:
Revision 13 as of 2010-11-04 08:40:13
Size: 18562
Editor: WeiZhong
Comment:
Deletions are marked like this. Additions are marked like this.
Line 5: Line 5:
[http://spaces.msn.com/members/shukebeta/Blog/cns!1psZEmYfE1uAECP30_KFiC1Q!128.entry 魏忠的Space发布] [[http://spaces.msn.com/members/shukebeta/Blog/cns!1psZEmYfE1uAECP30_KFiC1Q!128.entry|魏忠的Space发布]]
Line 7: Line 7:

::-- ZoomQuiet [[[DateTime(2006-01-09T06:12:38Z)]]]
[[
TableOfContents]]
= python中的 new-tyle 及其实例 =
(原文是python in a nutshell5.2节)
== 5.2新版类及其实例 ==

    
从python2.2起,如果一个类继承自内建的object类型(或者它子类了任何内建类型如list,dict,file等等),那它就是一个 新版的类
    
之前,不允许内建类别为基类生成新类,而且根本没有object这个类别。在本章5.4节的后半部分,我介绍了不管一个类是否有基类都能生一个新版类的其它方法

    在本章的开头,建议每个人养成使用新版类的编程习惯(当然你得用Python2.2以上版本)。
===
5.2.1 The Built-in object Type ===
    
objet 类是所有内建类及新版类的祖先。 object定义了一系列特殊方法(本章5.3节后半部分有它们的文档)来实现所有对象的默认行为。
    `__new__ , __init__`

    
你可以创建 object 的直接子类,这样静态方法 __new__()方法就用来创建类的实例,然后使用该实例的__init__()方法初始化这个实例__init__()方法会忽略你传递过来的任何参数。

    `__delattr__ , __getattribute__, __setattr__`

    
对象用这些方法来处理属性引用。本章前半部分已经做了详细介绍。
    `__hash__ , __repr__, __str__`

    一个对象可以作为参数传递给 hash函数 repr函数及 str 类。

    
object的子类可以覆盖这些方法,并且|或添加新的方法
===
5.2.2 Class-Level Methods ===
'''
级别方法'''
    
对象模型提供了两种类级别方法(传统对象模型没有这些方法):静态方法和类方法。类级别方法仅存python2.2以上。
    不过在python2.2
及更新版本中,传统对象已经拥有这些级别方法。新对象模型提供诸多新特性,仅有特性被传统对象模型全功能实现。

==== 5.2.2.1 Static methods ====
'''
5.2.2.1静态方法'''
    
静态方法可以直接被类或类实例调用。不具有常规方法那样的特殊行为(绑定、非绑定、第一个参数等)。完全可以将静态方法当成一个用属性引用方式调用的普通函数来看待。任何时候定义静态方法都不是必须的(你完全可以定义一个普通函数) 某些程序员认为,当有一堆函数仅仅为某一特定类编写时,这种方式可以提供一致性。

    以上言论过时,根据python2.4提供的新特性,你可以象下面这样做。
{{{#!python
    class AClass(object):
        @staticmethod #静态方法修饰符,表示下面的方法是一个静态方法
        def astatic( ): print 'a static method'
    anInstance = AClass( )
    AClass.astatic( ) # prints: a static method
    anInstance.astatic( ) # prints: a static method
}}}

    
上言论过.
====
5.2.2.2 Class methods ====

    
一个类方法就是你可以通过类或它的实例来调用,python将类绑定到该方法的第一个参数. 不管你是用类调用这个方法还是类的实例调用这个方法,方法的第一个参数都是类对外服而不是实例对象. 按照惯例,类方法的第一个形参被命名为 cls. 任何时候定义类方法都不是必须的(你完全可以定义一个以该类为参数的普通函数).某些程序员认为这个特性会提供一定的一致性.
    以上言论过时,根据python2.4提供的新特性,你可以象下面这样做。
{{{#!python
    class ABase(object):
        @classmethod #类方法修饰符
        def aclassmet(cls): print 'a class method for', cls.__name__
    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.2.3 New-Style Classes ===
''' 5.2.3 新版的类'''
    本章前面提到的传统类
的全部特性,版类同样都有.新版的类添加了关于__init__特殊方法的其它特性,版的类拥有一个名为 __new__的静态方法
==== 5.2.3.1 __init__ ====

    新版
C从 object 继承了没有被覆盖的 __init__方法.它允许你传递任意的参数调用C,只是这些参数都被丢弃而已. 你也许会觉得这很让人惊讶,呵呵,事实确实就是如此.
    我
建议你在所有的新版类中覆盖 __init__方法. 只有极少的情况你的类的 __init__方法无事可做.
{{{#!python
    class C(object):
        def __init__(self): pass
     # rest of class body omitted
}}}
     ==== 5.2.3.2 __new__ ====

    
每一个新版类都有一个名为`__new__`的静态方法. 当你调用 `C(*args,**kwds)`创建一个C实例时,python内部调用的是 `C.__new__(C,*args,**kwds).python` 使用`__new__`方法的返回值 x 做为新生成的实例返回. 在确认 x 是C的实例以后,
python调用`C.__init__(x,*args,**kwds)`来初始化实例. 也就是说,对新类C来讲,语句 x=C(23)等同于:{{{
    
x = C.__new__(C, 23)
    if isinstance(x, C): C.__init__(x, 23)
}}}
         `object.__new__`创建一个新的,未初始化的类实例,它接收传递过来的第一个参数(也就是类对象本身),忽略其它的参数.
当你覆盖`__new__`方法时,你不必使用函数修饰符` @staticmethod`, python解释器根据上下文会认出`__new__()`方法.
如果你需要重绑定` C.__new__`方法,在类外,你只需要使用` C.__new__=staticmethod(你想使用的新方法)` 即.(很少有这样的需求)

    `__new__`方法拥有函数工厂的绝大部分弹性. `__new__`根据实际情况可以返回一个已有的实例或者创建一个新的实例.
    下面举一个通过覆盖__new__方法实现独身对象的设计模式的例子:
{{{#!python
    class Singleton(object):
        _singletons = {}
     def __new__(cls, *args, **kwds):
            if not cls._singletons.has_key(cls): #若还没有任何实例
                cls._singletons[cls] = object.__new__(cls) #生成一个实例
            return cls._singletons[cls] #返回这个实例
}}}
    
Singleton的所有子类(当然是没有覆盖`__new__`方法的子类)都只可能有一个实例. 如果子类定义了一个`__init__`方法,
那么子类必须保它的`__init__`方法是多次对同一实例调用是安全的.

===
5.2.4 New-Style Instances ===
''' 5.2.4新版类
实例'''
    所有传统类
实例有特性,新版类实例全都有.新版类实例还拥有以下:
    
`__slots__`特殊属性影响着实例属性的访问
    
对象模型同样添加了一个新的方法` __getattribute__ `比原有的` __getattr__ `方法更通用. 不同的实例可以拥有这些特殊方法的不同实现.

==== 5.2.4.1 Properties ====
     
property 一个实例中具有特殊功能的属性. 你可以使用常规语法对property进行引用,绑定或解除绑定.如:
{{{    print x.prop
    x.prop=23
    del x.pro
}}}     下面介绍如何定义一个只读property
{{{#!python
    class Rectangle(object):
        def __init__(self, width, heigth):
            self.width = width
            self.heigth = heigth
        def getArea(self):
            return self.width * self.heigth
     area = property(getArea, doc='area of the rectangle')
}}}
         properties 能做的任务与那些特殊方法是相似的,不过更简单更快捷. 你通过调用内建property类别来生成一个property,并将其结果绑定为一个类属性.就象绑定类属性,通常定义内来创建property, 当然选择在其它地方.假设在新版类 C 定义内,使用以下语法:
{{{ attrib = property(fget=None, fset=None, fdel=None, doc=None)}}}

    
x是C的一个实例,当你引用 x.attrib 时,python调用 fget方法取值给你. 当你为x.attrib赋值: x.attrib=value 时,python调用 fset方法,并且value值做为fset方法的参数

    这一段是讲述如何
传统对象模型实现property,不看也罢,将来传统对象模型是要被丢弃的.
{{{#!python
    class Rectangle:
        def __init__(self, width, heigth):
            self.width = width
            self.heigth = heigth
        def getArea(self):
            return self.width * self.heigth
        def __getattr__(self, name):
            if name= ='area': return self.getArea( )
            raise AttributeError, name
        def __setattr__(self, name, value):
            if name= ='area':
                raise AttributeError, "can't bind attribute"
            self.__dict__[name] = value
}}}
==== 5.2.4.2 __slots__ ====

'''' 5.2.4.2 __slots__属性'''
    
通常,每个实例对象 x 都拥有一个字典 x.__dict__ python通过此字典允许你绑定任意属性给 x 对象. 为节省内存, 你可以定义一个 类属性 __slots__,这是一个字符串序列(通常是一个tuple) 当类 C 拥有 __slots__属性, x的直接子类就没有 x.__dict__属性. 如果试图绑定一个 __slots__中不存在的类型给实例的话,引发一个异常. 使用__slots__你失去绑定任意属性的能力,却能帮你节省内存消费,有助于生成小的实例对象.
    提醒: 当一个类会生成很多很多实例时,即一个实例节省几十个字节都可节省一大笔内存时,__slots__就值得使用.典型的类能有数百万而不是几千个实例同时存在.不象大多数其它的类属性, 就象我刚刚描述的那样,只有类定义语句可以绑定它做为类属性.任何后来的修改,重新绑定或解除绑定对__slots__不起作用.即使对从一个类继承过来的__slots__也一样. 下面介绍如何通过添加 __slots__属性给刚才定义的 Rectangle 类,以便得到瘦身的类实例:
{{{#!python
    class OptimizedRectangle(Rectangle):
     __slots__ = 'width', 'heigth'
}}}
    我们不需给area property也定义一个slot. __slots__里不能包含 properties, 只能包含常规实例属性. 如果不定义 __slots__属性,那些属性只能保存在实例的__dict__属性中.
==== 5.2.4.3 __getattribute__ ====
    
新版类的实例来说, 所有的属性引用都是通过特殊属性__getattribute__方法完成的.这个方法由基类对象提供,由它实现对象属性引用的全部细节.在本章的前面有详细的文档. 如果有特殊需求,你也可以覆盖 __getattribute__属性. 比如你的子类实例中隐藏某些父类的属性或方法.下面的例子演示了实现一个没有append方法的list类:
{{{#!python
    class listNoAppend(list):
     def __getattribute__(self, name):
            if name =  = 'append': raise AttributeError, name
            return list.__getattribute__(self, name)
}}}
    这个类的实例几乎与内建list对象完全相同,除了性能更差以外. 任何该类实例append方法的调用都会引发一个异常.
    {{{#!python
    class AttributeWatcher:
        def __init__(self):
            # note the caution to avoid triggering __setattr__, and the
            # emulation of Python's name-mangling for a private attribute
            self.__dict__['_AttributeWatcher__mydict']={ }
        def __getattr__(self, name):
            # as well as tracing every call, for demonstration purposes we
            # also fake "having" any requested attribute, EXCEPT special
            # methods (__getattr__ is also invoked to ask for them: check by
            # trying a few operations on an AttributeWatcher instance).
            print "getattr", name
            try: return self.__mydict[name]
            except KeyError:
                if name.startswith('__') and name.endswith('__'):
                    raise AttributeError, name
                else: return 'fake_'+name
        def __setattr__(self, name, value):
            print "setattr", name, value
            self.__mydict[name] = value
        def __delattr__(self, name):
            print "delattr", name
            try: del self.__mydict[name]
            except KeyError: pass
}}}

====
5.2.4.4 Per-instance methods ====

'''
个体实例方法'''
    经典
与新对象模型都允许一个实例绑定私有的属性和方法. 对一个方法来讲,和其它属性一样,一个私有的属性绑定将覆盖类定义中的同名属性:只要在实例中发现这个属性,就不再继续查找类义了.

    经典与
对象模型的不同之处于实例私有的特殊方法.
    在经典
对象模型中,个实例可以有效的覆盖由类义提供的特殊方法.
    
在新对象模型中,实例特殊方法的隐式调用总是依赖类定义的特殊方法而不是实例中新绑定的特殊方法.下面这个例子可以说明这一点:
{{{#!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] # results in:
    # Traceback (most recent call last):
    
# File "<stdin>", line 1, in ?
    # TypeError: unindexable object
}}}

    可以看到,在
调用n[23]时,这是一个隐式的__getitem__方法调用,因为NewStyle中并未定义该方法,所以引发了异常
   
不过如果你使用n.__getitem__(23)这种方式来显式调用特殊方法时,它还是可以工作的

===
5.2.5 新对象模型中的继承 ===
''' Inheritance in the New-Style Object Model'''
    
在新对象模型中,继承的使用方式与传统模型大致相同.一个主要的区别是新版类可以从一个内建类型中继承.

    新版的类
仍然支持多继承,不过只有多个内建类型是特殊设计并允许相互兼容的情况下才可以被继承.通常情况,一个新版只允许继承一个真实型.这意味着在多继承时,除object以外,至多一个内建类型可以是所有内建类型及新版类的超类.

==== 5.2.5.1 Method resolution order ====
'''
方法解析顺序:'''
    
在传统对象模型中,方法和属性按 从左至右 深度优先的顺序查找.显然,当多个父类继承自同一个基类时,这会产生我们不想要的结果.

    
举例来说, A是B和C的子类,而B和C继承自D,传统对象模型的的查找方法是 A-B-D-C-D. 由于Python先查找D后查找C,即使C对D中的方法进行了重定义,也只能使用D中定义的版本.因为这个继承模式,该问题会导致一些实际麻烦.

    在新对象模型中,所有类均直接或间生成子类对象.

   
当D是object 的直接子类时,对象模型的搜索顺序就变为 A-B-C-D

    
每个内建类型及新版的类均内建一个特殊的只读属性 __mro__ ,这是一个tuple,保存着方法解析类型. 通过类来引用 __mro__,通过实例.

====
5.2.5.2 Cooperative superclass method calling ====
''''
协作式调用超类方法''''

    
前面我们提丟,当一个子类覆盖了一个方法,这个覆盖的方法通常要调用被覆盖的方法.python传统对象模型惯用的方式用非绑定方法语法调用父类的同名方法.当多继承时,这种方法是有缺限的,见下例:
{{{#!python
    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()方法被调用了两次. 我们怎样才可以保证每个父类的实现均被顺序调用且仅仅调用一次呢?不采取点特殊措施这个问题很难解决.从python2.2起,提供了这样一个特殊手段. 那就是 super类型.super(aclass,obj)返回一个obj相关的特殊对象. 当我们调用该对象的一个属性或方法时,这一段真不好翻译,总之,这样就保证了每个父类的实现均被调用且仅仅调用一次了. 改写后的代码如下:
{{{#!python
    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( )
}}}
         如果你养成了总是使用superclass调用父类方法,你的类就能适应无论多复杂的继承结构.
::-- WeiZhong [<<DateTime(2009-08-25T10:46:38Z)>>]
::--
ZoomQuiet [<<DateTime(2006-01-09T06:12:38Z)>>]
<<
TableOfContents>>
= python中的 new-style class 及其实例详解 =
(原文见《Python In a Nutshell(2003)》5.2节)

==
5.2 new-style Class 及其实例  ==
前面我提到 python 2.2 中引入了 new-style 对象模型.
new-style class 及其实例与 Classic class 一样, 都是顶级对象。 它们可以拥有任意的属性, 通过调用类对象生成该类的一个实例对象. 在这一小节,我来向大家揭示新的对象模型及传统对象模型的不同.

 python 2.2 起,如果一个类继承自 object 对象(或者它任何内建类型如 list, dict, file 的子类),那它就是一个 new-style class 。在之前,Python 不允许通过继承内建类生成新类,根本没有 object 这个对象。在本章5.4节的后半部分,我介绍给大家一个方法将 Classic class 改造 new-style class

我建议每个人, 从现在开始只使用 new-style class (当然你得用 Python2.2 以上版本)。新的对象模型与传统对象模型相比, 有虽小却非常重要的优势, 可以说接近完美.

==
5.2.1 内建的 object 对象 ==
object 对象是所有内建类 new-style class 的祖先。 object 对象定义了一系列特殊方法(参见 5.3 节后半部分)实现所有对象的默认行为。

`__new__, __init__` 方法

你可以创建 object 的直接子类,静态方法 `__new__()`用来创建类的实例,
实例的 `__init__()` 方法用来初始化自己
默认的 `__init__()`
方法会忽略你传递过来的任何参数。

`__delattr__, __getattribute__, __setattr__ ` 方法

对象用这些方法来处理属性引用。本章前半部分已经做了详细介绍。

`__hash__, __repr__, __str__ ` 方法

`print(some
obj)` 会调用 `someobj.__str__` 如果 `__str__` 没有定义, 则调用 `__repr__`
`repr(someobj)` 会调用 `someobj.__repr__`

允许obj
ect的子类重载这些方法,或添加新方法。

==
5.2.2 类方法 ==

对象模型提供了两种类方法(传统对象模型没有这些方法):静态方法和类方法。只有 python2.2 及更新版本才支持方法.
需要提一下的是,
 python2.2 及更新版本中, Classic class 实现了类方法。新对象模型提供诸多新特性有且仅有类方法这一特性被传统对象模型全功能实现。

=== 5.2.2.1静态方法 ===

静态方法可以直接被类或类实例调用。它没有常规方法那样的特殊行为(绑定、非绑定、默认的第一个参数规则等等)。完全可以将静态方法当成一个用属性引用方式调用的普通函数来看待。任何时候定义静态方法都不是必须的(静态方法能实现的功能都可以通过定义一个普通函数来实现. 有些程序员认为,当有一堆函数仅仅为某一特定类编写时,采用类方法这种方式能够提供足够的一致性(和一定程度的 namespace 的功能)

根据python2.4提供的新的语法,你可以象下面这样来创建一个静态方法,
{{{#!python
class AClass(object):
    @staticmethod       #静态方法修饰符,表示下面的方法是一个静态方法
    def astatic( ): print 'a static method'
anInstance = AClass( )
AClass.astatic( ) # prints: a static method
anInstance.astatic( ) # prints: a static method
}}}
注:staticmethod是一个内建函数, 用来将一个方法包装成静态方法, 在2.4前版本, 要用下面的方式定义一个静态方法(不再推荐使用):
{{{#!python
class AClass(object):
    def astatic( ): print 'a static method'
    astatic=staticmethod(astatic)
}}}
这种方法在函数定义本身比较长
经常会忘记后面这一行.

===
5.2.2.2 类方法 ===

一个类方法就是你可以通过类或它的实例来调用的方法, 不管你是用类调用这个方法还是类的实例调用这个方法,python只会将实际的类对象做为该方法的第一个参数.记住:方法的第一个参数都是类对而不是实例对象. 按照惯例,类方法的第一个形参被命名为 cls. 任何时候定义类方法都不是必须的(静态方法能实现的功能都可以通过定义一个普通函数来实现,只要这个函数接受一个类对象做为参数就可以了).某些程序员认为这个特性当有一堆函数仅仅为某一特定类编写时会提供使用上的一致性.

定义类方法:

{{{#!python
class ABase(object):
    @classmethod        #类方法修饰符
    def aclassmet(cls): print 'a class method for', cls.__name__
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
}}}
注:classmethod是一个内建函数,用来将一个方法封装成类方法,在2.4前版本,你只能用下面的方式定义一个类方法:
{{{#!python
class AClass(object):
    def aclassmethod(cls): print 'a class method'
    aclassmethod=staticmethod(aclassmethod)
}}}
并没有人要求必须封装后的方法名字必须与封装前一致,但建议你总是这样做(如果你使用python2.4版本以下
).
这种方法在函数定义本身比较长时经常会忘记后面这一行.

==
5.2.3 new-style class ==
除了拥有
Classic class 的全部特性之外, new-style class 当然还具有一些特性.`__init__`特殊方法的行为与 Classic class 相比有了一些变化, 另外还增了一个名为 `__new__` 的静态方法

===
5.2.3.1 __init__方法 ===

下面的 C
`(一个 new-style class)`中, 从 object 继承来的原始 `__init__`方法, 可以认为就是一个 pass 语句, 因为它几乎什么都不做, 建议你在所有的 new-style class 中重实现 `__init__` 方法.
{{{#!python
class C(object):
    def __init__(self): pass
    # rest of class body omitted
}}}

示例中的的类只允许无参数调用,硬要传递一个参数给它会产生异常`(如用C('xyz'))`. 如果C没有重载`__init__`方法, 调用C('xyz')会象 'xyz' 根本不存在一样忽略参数继续执行.
注意:
(根据我的试验,2.4版中这点发生了变化,即使没有重载`__init__`方法,象C('xyz')这样调用一样会引发异常)

===
5.2.3.2 __new__方法 ===

每一个 new-style class 都有一个名为`__new__`的静态方法. 当你调用 C(*args,**kwds)创建一个C实例时,python内部调用的是 C.`__new__`(C,*args,**kwds).

`__new__`方法的返回值 x 就是该类的实例. 在确认 x 是C的实例以后, python调用C.`__init__`(x,*args,**kwds)来初始化这个实例. 也就是说,对新类C来讲,语句 x=C(23)等同于:
{{{#!python
x = C.__new__(C, 23)
if isinstance(x, C): C.__init__(x, 23)
}}}
{{{
object.__new__创建一个新的,未初始化的类实例,它接收传递过来的第一个参数(也就是类对象本身),忽略其它的参数.当你重载__new__方法时,你不必使用函数修饰符@staticmethod, python解释器根据上下文会认出__new__()方法是一个静态方法. 如果你需要重绑定 C.__new__方法,你只需要在类外面执行 C.__new__=staticmethod(你想使用的新方法)以了.(极少有这样的需求)

__new__方法拥有函数工厂的绝大部分弹性. 根据实际需求,我们可以让__new__返回一个已有的实例或者创建一个新的实例.
下面举一个通过重载__new__方法实现独身对象的设计模式的例子:}}}
{{{#!python
class Singleton(object):
    _singletons = {}
    def __new__(cls, *args, **kwds):
        if not cls._singletons.has_key(cls):          #若还没有任何实例
            cls._singletons[cls] = object.__new__(cls)  #生成一个实例
        return cls._singletons[cls]                          #返回这个实例
}}}{{{
Singleton的所有子类(当然是没有重载__new__方法的子类)都只可能有一个实例. 如果该类的子类定义了一个__init__方法,那么必须保它的__init__方法能够安全的对同一实例进行多次调用.
}}}
==
5.2.4 new-style class 实例  ==
 new-style class
实例除了拥 Classic class 实例的全部特性之外,还拥有一种称为property的及一个叫作`__slots__`特殊属性,该属性会对实例其它属性的访问产生重要影响.
对象模型同样添加了一个新的方法 `__getattribute__ 比原有的 __getattr__ `方法更通用. 不同的实例可以拥有这些特殊方法的不同实现.

=== 5.2.4.1 Properties  ===
property 是实例中具有特殊功能的属性. 你可以使用常规语法对property进行引用,绑定或解除绑定.如:
{{{#!python
print x.prop
x.prop=23
del x.prop
}}}
然而,property如果只有这点功能那就和普通属性没什么两样了,property有它的独到之处,请往下读.
下面介绍如何定义一个只读property:
{{{#!python
class Rectangle(object):
    def __init__(self, width, heigth):
        self.width = width
        self.heigth = heigth
    def getArea(self):
        return self.width * self.heigth
    area = property(getArea, doc='area of the rectangle')
}}}
{{{
矩形类的每一个实例 r 均拥有一个只读属性 r.area, 该属性由 r.getArea()方法实时计算得来. Rectangle.area.__doc__是'area of the rectangle', 这个属性是只读的(试图对它进行重绑定或解除绑定的企图都注定会失败), 这是因为我们在property定义中指定了该属性的 get 方法.

properties 干的活
与那些特殊方法__getattr__, __setattr__, __delattr__ 等极其相似的, 不过同样的活它干起来更简单更快捷. 内建 property 类别(我倒宁愿把当成一个函数来看)用来生成一个 property, 并将其返回值绑定为一个类属性. 如同绑定类的常规属性, 一般在定义类时就创建property, 当然也有其它选择. 假设在定义 new-style class C 时, 使用以下语法:

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

x  C 的一个实例, 当你引用 x.attrib 时, python调用 fget 方法取值给你. 当你为x.attrib赋值: x.attrib=value 时, python调用 fset方法, 并且value值做为fset方法的参数, 当你执行del x.attrib 时, python调用fdel方法, 你传过去的名为 doc 的参数即为该属性的文档字符串. 矩形类,因为我们没有为area属性指定 fset 和 fdel 参数, 所以该属性只能读取.
}}}

=== 5.2.4.2 __slots__属性 ===

{{{
通常, 每个实例对象 x 都拥有一个字典 x.__dict__. python通过此字典允许你绑定任意属性给 x 实例. 定义一个名为 __slots__ 的类属性可以有效减少每个实例占用的内存数量. __slots__ 是一个字符串序列(通常是一个tuple). 当类 C 拥有 __slots__属性, x 的直接子类就没有 x.__dict__属性. 如果试图绑定一个 __slots__ 中不存在属性给实例的话, 就会引发异常. __slots__属性虽然令你失去绑定任意属性的方便, 却能有效节省每个实例的内存消耗, 有助于生成小而精干的实例对象.
: 当一个类会生成很多很多实例时(有些类同时拥有数百万而不是几千个实例), 使一个实例节省几十个字节都可节省一大笔内存时, 就值得使用__slots__属性. 只有类定义中可以使用 __slots__ = aTuple 语句一个添加__slots__属性, 其它任何位置对一个类或其父类的__slots__属性的修改, 重新绑定或解除绑定都是无效的.
下面介绍如何通过添加 __slots__ 属性给刚才定义的 Rectangle 类, 以得到瘦身的类实例:
}}}

{{{#!python
class OptimizedRectangle(Rectangle):
    __slots__ = 'width', 'heigth'
}}}
{{{
__slots__里不能包含 properties, 只能包含常规实例属性. 我们不需也不允许给area property 也定义一个slot. 若不定义 __slots__属性, 常规属性保存在实例的__dict__属性中.
}}}
{{{
__slot__ 只是用来占位,因此对于__slot__ 定义的属性名,你首先要赋值,然后才可以使用。直接使用是会报错的。 -- Limodou
}}}

===
5.2.4.3 __getattribute__方法 ===

{{{
 new-style class 的实例来说, 所有的属性引用都是通过特殊方法 __getattribute__() 完成的. 该方法由基类对象提供, 负责实现对象属性引用的全部细节. 在本章的前面有该方法详细的文档. 如果有特殊需求,你也可以重载 __getattribute__ 属性(比如你打算在子类实例中隐藏父类的某些属性或方法).下面的例子演示了实现一个没有 append 方法的 list 类:
}}}

{{{#!python
class listNoAppend(list):
    def __getattribute__(self, name):
        if name == 'append': raise AttributeError, name
        return list.__getattribute__(self, name)
}}}
除了功能不全以外, 该类的实例与内建list对象完全相同. 任何调用该类实例append方法的企图都会引发一个异常.

===
5.2.4.4个体实例方法  ===

传统
与新对象模型都允许一个实例拥有私有的属性和方法(通过绑定或重绑定) . 实例的私有属性会屏蔽掉类定义中的同名属性.举例来说:
{{{#!python
class abc(object):
        def attrib_a(self):
                print 'aMethod defined in class abc'
b = abc()
def afunc():
        print 'hello,world!'
b.attrib_a=afunc
b.attrib_a()
}}}
该例子将打印 'hello,world!'

 python 隐式调用实例的私(后绑)特殊方法时, 对象模型的行为与传统对象模型不同. 传统对象模型中, 无论是显式调用, 还是隐式调用, 都会调用这个实例的后绑定特殊方法. 而在新对象模型中, 除非显式调用实例的特殊方法, 否则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] # 程序执行到这步会出错. 如果将代码改为 print n.__getitem__(23) 则正常运行

# Traceback (most recent call last)
:
# File "<stdin>", line 1, in ?
# TypeError: unindexable object
}}}
{{{
调用n[23],将产生一个隐式的__getitem__方法调用,因为 new-style class 对象 n 中并未定义该方法, 所以引发了异常. 不过如果你使用n.__getitem__(23)这种方式来显式调用特殊方法时, 它还是可以工作的.
}}}

==
5.2.5 新对象模型中的继承  ==

在新对象模型中, 继承的使用方式与传统模型大致相同. 一个关键的区别 new-style class 能从一个内建类型中继承而 Classic class 不能.
 new-style class
仍然支持多继承, 若要从多个内建类型继承生成一个新类, 则这些内建类型必须经过特殊设计能够相互兼容. python不支持随意的从多个内建类型进行多继承, 通常情况都是通过至多从一个内建继承得到新. 这意味着在多继承时, 除object以外, 至多一个内建类型可以是其它内建类型和 new-style class 的超类.

=== 5.2.5.1方法解析顺序:  ===
在传统对象模型中, 方法和属性按 从左至右 深度优先 的顺序查找. 显然, 当多个父类继承自同一个基类时, 这会产生我们不想要的结果.
举例来说, A  B  C 的子类,  B  C 继承自 D,传统对象模型的的属性查找方法是 A - B - D - C - D. 由于Python先查找 D 后查找 C, 即使 C  D 中的方法进行了重定义, 也只能使用 D 中定义的版本. 由于这个继承模式固有的问题, 在实际应用中造成一些麻烦.

在新对象模型中,所有类均直接或间生成子类对象. python改变了传统对象模型中的解析顺序, 使用上面的例子, D 一个 new-style class (比如 D 是 object 的直接子类), 对象模型的搜索顺序就变为 A - B - C - D.

每个内建类型及 new-style class 均内建一个特殊的只读属性 `__mro__ `, 这是一个tuple, 保存着方法解析类型. 只允许通过类来引用 `__mro__` (允许通过实例).

===
5.2.5.2 协作式调用超类方法  ===
前面我们提到, 当一个子类重载父类中一个方法, 子类中的方法通常要调用父类中的同名方法来做一些事. 这也是python传统对象模型惯用的方式, 即使用非绑定方法语法调用父类的同名方法. 当多继承时, 这种方法是有缺陷的, 见下例:
{{{#!python
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()方法被调用了两次. 我们怎样才可以保证每个父类的实现均被顺序调用且仅仅调用一次呢? 不采取点特殊措施这个问题很难解决.  python2.2 起, 提供了这样一个特殊手段. 那就是 super类型. super(aclass,obj) 返回对象 obj 的一个特殊的超对象 (superobject). 当我们调用该对象的一个属性或方法时, 就保证了每个父类的实现均被调用且仅仅调用一次了. 改写后的代码如下:
{{{#!python
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( )
}}}

现在就可以得到期望的结果了.
如果你养成了总是使用superclass调用父类方法,你的类就能适应无论多复杂的继承结构.

魏忠的Space发布 ::-- WeiZhong [2009-08-25 10:46:38] ::-- ZoomQuiet [2006-01-09 06:12:38]

1. python中的 new-style class 及其实例详解

(原文见《Python In a Nutshell(2003)》5.2节)

1.1. 5.2 new-style Class 及其实例

前面我提到 python 2.2 中引入了 new-style 对象模型. new-style class 及其实例与 Classic class 一样, 都是顶级对象。 它们可以拥有任意的属性, 通过调用类对象生成该类的一个实例对象. 在这一小节,我来向大家揭示新的对象模型及传统对象模型的不同.

从 python 2.2 起,如果一个类继承自 object 对象(或者它是任何内建类型如 list, dict, file 的子类),那它就是一个 new-style class 。在此之前,Python 不允许通过继承内建类型生成新类,也根本没有 object 这个对象。在本章5.4节的后半部分,我会介绍给大家一个方法,将 Classic class 改造成 new-style class 。

我建议每个人, 从现在开始只使用 new-style class (当然你得用 Python2.2 以上版本)。新的对象模型与传统对象模型相比, 有虽小却非常重要的优势, 可以说接近完美.

1.2. 5.2.1 内建的 object 对象

object 对象是所有内建类型及 new-style class 的祖先。 object 对象定义了一系列特殊方法(参见 5.3 节后半部分)实现所有对象的默认行为。

__new__, __init__ 方法

你可以创建 object 的直接子类,静态方法 __new__()用来创建类的实例, 实例的 __init__() 方法用来初始化自己。 默认的 __init__() 方法会忽略你传递过来的任何参数。

__delattr__, __getattribute__, __setattr__  方法

对象用这些方法来处理属性引用。本章前半部分已经做了详细介绍。

__hash__, __repr__, __str__  方法

print(someobj) 会调用 someobj.__str__ 如果 __str__ 没有定义, 则调用 __repr__ repr(someobj) 会调用 someobj.__repr__

允许object的子类重载这些方法,或添加新方法。

1.3. 5.2.2 类方法

新的对象模型提供了两种类方法(传统对象模型没有这些方法):静态方法和类方法。只有 python2.2 及更新版本才支持类方法. 需要提一下的是,在 python2.2 及更新版本中, Classic class 也实现了类方法。新的对象模型提供的诸多新特性中,有且仅有类方法这一特性被传统对象模型全功能实现。

1.3.1. 5.2.2.1静态方法

静态方法可以直接被类或类实例调用。它没有常规方法那样的特殊行为(绑定、非绑定、默认的第一个参数规则等等)。完全可以将静态方法当成一个用属性引用方式调用的普通函数来看待。任何时候定义静态方法都不是必须的(静态方法能实现的功能都可以通过定义一个普通函数来实现). 有些程序员认为,当有一堆函数仅仅为某一特定类编写时,采用类方法这种方式能够提供足够的一致性(和一定程度的 namespace 的功能)。

根据python2.4提供的新的语法,你可以象下面这样来创建一个静态方法,

   1 class AClass(object):
   2     @staticmethod       #静态方法修饰符,表示下面的方法是一个静态方法
   3     def astatic(  ): print 'a static method'
   4 anInstance = AClass(  )
   5 AClass.astatic(  )                    # prints: a static method
   6 anInstance.astatic(  )                # prints: a static method

注:staticmethod是一个内建函数, 用来将一个方法包装成静态方法, 在2.4以前版本, 要用下面的方式定义一个静态方法(不再推荐使用):

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

这种方法在函数定义本身比较长时经常会忘记后面这一行.

1.3.2. 5.2.2.2 类方法

一个类方法就是你可以通过类或它的实例来调用的方法, 不管你是用类调用这个方法还是类的实例调用这个方法,python只会将实际的类对象做为该方法的第一个参数.记住:方法的第一个参数都是类对象而不是实例对象. 按照惯例,类方法的第一个形参被命名为 cls. 任何时候定义类方法都不是必须的(静态方法能实现的功能都可以通过定义一个普通函数来实现,只要这个函数接受一个类对象做为参数就可以了).某些程序员认为这个特性当有一堆函数仅仅为某一特定类编写时会提供使用上的一致性.

定义类方法:

   1 class ABase(object):
   2     @classmethod        #类方法修饰符
   3     def aclassmet(cls): print 'a class method for', cls.__name__
   4 class ADeriv(ABase): pass
   5 bInstance = ABase(  )
   6 dInstance = ADeriv(  )
   7 ABase.aclassmet(  )               # prints: a class method for ABase
   8 bInstance.aclassmet(  )           # prints: a class method for ABase
   9 ADeriv.aclassmet(  )              # prints: a class method for ADeriv
  10 dInstance.aclassmet(  )           # prints: a class method for ADeriv

注:classmethod是一个内建函数,用来将一个方法封装成类方法,在2.4以前版本,你只能用下面的方式定义一个类方法:

   1 class AClass(object):
   2     def aclassmethod(cls): print 'a class method'
   3     aclassmethod=staticmethod(aclassmethod)

并没有人要求必须封装后的方法名字必须与封装前一致,但建议你总是这样做(如果你使用python2.4版本以下时). 这种方法在函数定义本身比较长时经常会忘记后面这一行.

1.4. 5.2.3 new-style class

除了拥有 Classic class 的全部特性之外, new-style class 当然还具有一些新特性.__init__特殊方法的行为与 Classic class 相比有了一些变化, 另外还新增了一个名为 __new__ 的静态方法

1.4.1. 5.2.3.1 __init__方法

下面的 C 类(一个 new-style class)中, 从 object 继承来的原始 __init__方法, 可以认为就是一个 pass 语句, 因为它几乎什么都不做, 建议你在所有的 new-style class 中重新实现 __init__ 方法.

   1 class C(object):
   2     def __init__(self): pass
   3     # rest of class body omitted

示例中的的类只允许无参数调用,硬要传递一个参数给它会产生异常(如用C('xyz')). 如果C没有重载__init__方法, 调用C('xyz')会象 'xyz' 根本不存在一样忽略参数继续执行. 注意: (根据我的试验,2.4版中这点发生了变化,即使没有重载__init__方法,象C('xyz')这样调用一样会引发异常)

1.4.2. 5.2.3.2 __new__方法

每一个 new-style class 都有一个名为__new__的静态方法. 当你调用 C(*args,**kwds)创建一个C实例时,python内部调用的是 C.__new__(C,*args,**kwds).

__new__方法的返回值 x 就是该类的实例. 在确认 x 是C的实例以后, python调用C.__init__(x,*args,**kwds)来初始化这个实例. 也就是说,对新类C来讲,语句 x=C(23)等同于:

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

object.__new__创建一个新的,未初始化的类实例,它接收传递过来的第一个参数(也就是类对象本身),忽略其它的参数.当你重载__new__方法时,你不必使用函数修饰符@staticmethod, python解释器根据上下文会认出__new__()方法是一个静态方法. 如果你需要重绑定 C.__new__方法,你只需要在类外面执行 C.__new__=staticmethod(你想使用的新方法)就可以了.(极少有这样的需求)

__new__方法拥有函数工厂的绝大部分弹性. 根据实际需求,我们可以让__new__返回一个已有的实例或者创建一个新的实例.
下面举一个通过重载__new__方法实现独身对象的设计模式的例子:

   1 class Singleton(object):
   2     _singletons = {}
   3     def __new__(cls, *args, **kwds):
   4         if not cls._singletons.has_key(cls):            #若还没有任何实例
   5             cls._singletons[cls] = object.__new__(cls)  #生成一个实例
   6         return cls._singletons[cls]                             #返回这个实例

Singleton的所有子类(当然是没有重载__new__方法的子类)都只可能有一个实例. 如果该类的子类定义了一个__init__方法,那么它必须保证它的__init__方法能够安全的对同一实例进行多次调用.

1.5. 5.2.4 new-style class 实例

  • new-style class 实例除了拥有 Classic class 实例的全部特性之外,还拥有一种称为property的新属性及一个叫作__slots__的特殊属性,该属性会对实例其它属性的访问产生重要影响.

新的对象模型同样添加了一个新的方法 __getattribute__ 比原有的 __getattr__ 方法更通用. 不同的实例可以拥有这些特殊方法的不同实现.

1.5.1. 5.2.4.1 Properties

property 是实例中具有特殊功能的属性. 你可以使用常规语法对property进行引用,绑定或解除绑定.如:

   1 print x.prop
   2 x.prop=23
   3 del x.prop

然而,property如果只有这点功能那就和普通属性没什么两样了,property有它的独到之处,请往下读. 下面介绍如何定义一个只读property:

   1 class Rectangle(object):
   2     def __init__(self, width, heigth):
   3         self.width = width
   4         self.heigth = heigth
   5     def getArea(self):
   6         return self.width * self.heigth
   7     area = property(getArea, doc='area of the rectangle')

矩形类的每一个实例 r 均拥有一个只读属性 r.area, 该属性由 r.getArea()方法实时计算得来. Rectangle.area.__doc__是'area of the rectangle', 这个属性是只读的(试图对它进行重绑定或解除绑定的企图都注定会失败), 这是因为我们在property定义中指定了该属性的 get 方法.

properties 干的活与那些特殊方法__getattr__, __setattr__, __delattr__ 等是极其相似的, 不过同样的活它干起来更简单更快捷. 内建 property 类别(我倒宁愿把当成一个函数来看)用来生成一个 property, 并将其返回值绑定为一个类属性. 如同绑定类的常规属性, 一般在定义类时就创建property, 当然也有其它选择. 假设在定义 new-style class  C 时, 使用以下语法:

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

x 是 C 的一个实例, 当你引用 x.attrib 时, python调用 fget 方法取值给你. 当你为x.attrib赋值: x.attrib=value 时, python调用 fset方法, 并且value值做为fset方法的参数, 当你执行del x.attrib 时, python调用fdel方法, 你传过去的名为 doc 的参数即为该属性的文档字符串. 在矩形类中,因为我们没有为area属性指定 fset 和 fdel 参数, 所以该属性只能读取.

1.5.2. 5.2.4.2 __slots__属性

通常, 每个实例对象 x 都拥有一个字典 x.__dict__. python通过此字典允许你绑定任意属性给 x 实例. 定义一个名为 __slots__ 的类属性可以有效减少每个实例占用的内存数量. __slots__ 是一个字符串序列(通常是一个tuple).  当类 C 拥有 __slots__属性, x 的直接子类就没有 x.__dict__属性. 如果试图绑定一个 __slots__ 中不存在属性给实例的话, 就会引发异常. __slots__属性虽然令你失去绑定任意属性的方便, 却能有效节省每个实例的内存消耗, 有助于生成小而精干的实例对象.
注: 当一个类会生成很多很多实例时(有些类同时拥有数百万而不是几千个实例), 即使一个实例节省几十个字节都可节省一大笔内存时, 就值得使用__slots__属性. 只有在类定义中可以使用 __slots__ = aTuple 语句来为一个类添加__slots__属性, 其它任何位置对一个类或其父类的__slots__属性的修改, 重新绑定或解除绑定都是无效的.
下面介绍如何通过添加 __slots__ 属性给刚才定义的 Rectangle 类, 以得到瘦身的类实例:

   1 class OptimizedRectangle(Rectangle):
   2     __slots__ = 'width', 'heigth'

__slots__里不能包含 properties, 只能包含常规实例属性. 我们不需也不允许给area property 也定义一个slot.  若不定义 __slots__属性, 常规属性则保存在实例的__dict__属性中.

__slot__ 只是用来占位,因此对于__slot__ 定义的属性名,你首先要赋值,然后才可以使用。直接使用是会报错的。 -- Limodou

1.5.3. 5.2.4.3 __getattribute__方法

对 new-style class 的实例来说, 所有的属性引用都是通过特殊方法 __getattribute__() 完成的. 该方法由基类对象提供, 负责实现对象属性引用的全部细节. 在本章的前面有该方法详细的文档. 如果有特殊需求,你也可以重载 __getattribute__ 属性(比如你打算在子类实例中隐藏父类的某些属性或方法).下面的例子演示了实现一个没有 append 方法的 list 类:

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

除了功能不全以外, 该类的实例与内建list对象完全相同. 任何调用该类实例append方法的企图都会引发一个异常.

1.5.4. 5.2.4.4个体实例方法

传统与新的对象模型都允许一个实例拥有私有的属性和方法(通过绑定或重绑定) . 实例的私有属性会屏蔽掉类定义中的同名属性.举例来说:

   1 class abc(object):
   2         def attrib_a(self):
   3                 print 'aMethod defined in class abc'
   4 b = abc()
   5 def afunc():
   6         print 'hello,world!'
   7 b.attrib_a=afunc
   8 b.attrib_a()

该例子将打印 'hello,world!'

在 python 隐式调用实例的私有(后绑定)特殊方法时, 新的对象模型的行为与传统对象模型不同. 在传统对象模型中, 无论是显式调用, 还是隐式调用, 都会调用这个实例的后绑定特殊方法. 而在新的对象模型中, 除非显式调用实例的特殊方法, 否则python总是去调用在类中定义的特殊方法. 下面这个例子可以说明这一点:

   1 def fakeGetItem(idx): return idx
   2 class Classic: pass
   3 c = Classic(  )
   4 c.__getitem__ = fakeGetItem
   5 print c[23]                       # prints: 23
   6 
   7 class NewStyle(object): pass
   8 n = NewStyle(  )
   9 n.__getitem__ = fakeGetItem
  10 print n[23]                       # 程序执行到这步会出错. 如果将代码改为 print n.__getitem__(23) 则正常运行
  11 
  12 # Traceback (most recent call last):
  13 #   File "<stdin>", line 1, in ?
  14 # TypeError: unindexable object

调用n[23],将产生一个隐式的__getitem__方法调用,因为 new-style class 对象 n 中并未定义该方法, 所以引发了异常. 不过如果你使用n.__getitem__(23)这种方式来显式调用特殊方法时, 它还是可以工作的.

1.6. 5.2.5 新的对象模型中的继承

在新的对象模型中, 继承的使用方式与传统模型大致相同. 一个关键的区别就是 new-style class 能从一个内建类型中继承而 Classic class 不能.

  • new-style class 仍然支持多继承, 若要从多个内建类型继承生成一个新类, 则这些内建类型必须是经过特殊设计能够相互兼容. python不支持随意的从多个内建类型进行多继承, 通常情况都是通过至多从一个内建类型继承得到新类. 这意味着在多继承时, 除object以外, 至多有一个内建类型可以是其它内建类型和 new-style class 的超类.

1.6.1. 5.2.5.1方法解析顺序:

在传统对象模型中, 方法和属性按 从左至右 深度优先 的顺序查找. 显然, 当多个父类继承自同一个基类时, 这会产生我们不想要的结果. 举例来说, A 是 B 和 C 的子类, 而 B 和 C 继承自 D,传统对象模型的的属性查找方法是 A - B - D - C - D. 由于Python先查找 D 后查找 C, 即使 C 对 D 中的方法进行了重定义, 也只能使用 D 中定义的版本. 由于这个继承模式固有的问题, 在实际应用中会造成一些麻烦.

在新的对象模型中,所有类均直接或间接生成子类对象. python改变了传统对象模型中的解析顺序, 使用上面的例子, 当 D 是一个 new-style class (比如 D 是 object 的直接子类), 新的对象模型的搜索顺序就变为 A - B - C - D.

每个内建类型及 new-style class 均内建一个特殊的只读属性 __mro__ , 这是一个tuple, 保存着方法解析类型. 只允许通过类来引用 __mro__ (不允许通过实例).

1.6.2. 5.2.5.2 协作式调用超类方法

前面我们提到, 当一个子类重载了父类中一个方法, 子类中的方法通常要调用父类中的同名方法来做一些事. 这也是python传统对象模型惯用的方式, 即使用非绑定方法语法调用父类的同名方法. 当多继承时, 这种方法是有缺陷的, 见下例:

   1 class A(object):
   2     def met(self):
   3         print 'A.met'
   4 class B(A):
   5     def met(self):
   6         print 'B.met'
   7         A.met(self)
   8 class C(A):
   9     def met(self):
  10         print 'C.met'
  11         A.met(self)
  12 class D(B,C):
  13     def met(self):
  14         print 'D.met'
  15         B.met(self)
  16         C.met(self)

在上面的代码中, 当我们调用 D().met()方法时, A.met()方法被调用了两次. 我们怎样才可以保证每个父类的实现均被顺序调用且仅仅调用一次呢? 不采取点特殊措施这个问题很难解决. 从 python2.2 起, 提供了这样一个特殊手段. 那就是 super类型. super(aclass,obj) 返回对象 obj 的一个特殊的超对象 (superobject). 当我们调用该超对象的一个属性或方法时, 就保证了每个父类的实现均被调用且仅仅调用一次了. 改写后的代码如下:

   1 class A(object):
   2     def met(self):
   3         print 'A.met'
   4 class B(A):
   5     def met(self):
   6         print 'B.met'
   7         super(B,self).met(  )
   8 class C(A):
   9     def met(self):
  10         print 'C.met'
  11         super(C,self).met(  )
  12 class D(B,C):
  13     def met(self):
  14         print 'D.met'
  15         super(D,self).met(  )

现在就可以得到期望的结果了. 如果你养成了总是使用superclass调用父类方法,你的类就能适应无论多复杂的继承结构.

PyNewStyleClass (last edited 2010-11-04 08:40:13 by WeiZhong)