[http://spaces.msn.com/members/shukebeta/Blog/cns!1psZEmYfE1uAECP30_KFiC1Q!128.entry 魏忠的Space发布]
::-- ZoomQuiet [DateTime(2006-01-09T06:12:38Z)] TableOfContents
1. python中的 new-tyle 类及其实例
(原文是python in a nutshell5.2节)
1.1. 5.2新版类及其实例
- 从python2.2起,如果一个类继承自内建的object类型(或者它子类了任何内建类型如list,dict,file等等),那它就是一个 新版的类。 在这之前,不允许以内建类别为基类生成新的类,而且根本没有object这个类别。在本章5.4节的后半部分,我介绍了不管一个类是否有基类,都能生成一个新版类的其它方法。 在本章的开头,我就建议每个人养成使用新版类的编程习惯(当然你得用Python2.2以上版本)。
1.1.1. 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的子类可以覆盖这些方法,并且|或添加新的方法
1.1.2. 5.2.2 Class-Level Methods
类级别方法
- 新版对象模型提供了两种类级别方法(传统对象模型没有这些方法):静态方法和类方法。类级别方法仅存在于python2.2以上。 不过在python2.2及更新版本中,传统对象也已经拥有这些类级别方法。新版对象模型提供了诸多新特性,仅有此特性被传统对象模型全功能实现。
1.1.2.1. 5.2.2.1 Static methods
5.2.2.1静态方法
- 静态方法可以直接被类或类实例调用。不具有常规方法那样的特殊行为(绑定、非绑定、第一个参数等)。你完全可以将静态方法当成一个用属性引用方式调用的普通函数来看待。任何时候定义静态方法都不是必须的(你完全可以定义一个普通函数) 某些程序员认为,当有一堆函数仅仅为某一特定类编写时,这种方式可以提供一致性。 以上言论过时,根据python2.4提供的新特性,你可以象下面这样做。
- 以上言论过时.
1.1.2.2. 5.2.2.2 Class methods
- 一个类方法就是你可以通过类或它的实例来调用,python将类绑定到该方法的第一个参数. 不管你是用类调用这个方法还是类的实例调用这个方法,方法的第一个参数都是类对外服而不是实例对象. 按照惯例,类方法的第一个形参被命名为 cls. 任何时候定义类方法都不是必须的(你完全可以定义一个以该类为参数的普通函数).某些程序员认为这个特性会提供一定的一致性. 以上言论过时,根据python2.4提供的新特性,你可以象下面这样做。
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
- 以上言论过时.
1.1.3. 5.2.3 New-Style Classes
5.2.3 新版的类
本章前面提到的传统类的全部特性,新版类同样都有.新版的类添加了关于init特殊方法的其它特性,新版的类拥有一个名为 new的静态方法
1.1.3.1. 5.2.3.1 __init__
新版类C从 object 继承了没有被覆盖的 init方法.它允许你传递任意的参数来调用C,只是这些参数都被丢弃而已. 你也许会觉得这很让人惊讶,呵呵,事实确实就是如此. 我建议你在所有的新版类中覆盖 init方法. 只有极少的情况你的类的 init方法无事可做.
1.1.3.2. 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方法实现独身对象的设计模式的例子:
Singleton的所有子类(当然是没有覆盖__new__方法的子类)都只可能有一个实例. 如果子类定义了一个__init__方法,
那么子类必须确保它的__init__方法是多次对同一实例调用是安全的.
1.1.4. 5.2.4 New-Style Instances
5.2.4新版类实例
- 所有传统类实例有的特性,新版类实例全都有.新版类实例还拥有以下新特性:
__slots__特殊属性影响着实例属性的访问 新版对象模型同样添加了一个新的方法 __getattribute__ 比原有的 __getattr__ 方法更通用. 不同的实例可以拥有这些特殊方法的不同实现.
1.1.4.1. 5.2.4.1 Properties
- property 就是一个实例中具有特殊功能的属性. 你可以使用常规语法对property进行引用,绑定或解除绑定.如:
{{{ print x.prop
- x.prop=23 del x.pro
}}}
- 下面介绍如何定义一个只读property
- 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,不看也罢,将来传统对象模型是要被丢弃的.
1 class Rectangle:
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 def __getattr__(self, name):
8 if name= ='area': return self.getArea( )
9 raise AttributeError, name
10 def __setattr__(self, name, value):
11 if name= ='area':
12 raise AttributeError, "can't bind attribute"
13 self.__dict__[name] = value
1.1.4.2. 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 类,以便得到瘦身的类实例:
我们不需给area property也定义一个slot. slots里不能包含 properties, 只能包含常规实例属性. 如果不定义 slots属性,那些属性只能保存在实例的dict属性中.
1.1.4.3. 5.2.4.3 __getattribute__
对新版类的实例来说, 所有的属性引用都是通过特殊属性getattribute方法完成的.这个方法由基类对象提供,由它实现对象属性引用的全部细节.在本章的前面有详细的文档. 如果有特殊需求,你也可以覆盖 getattribute属性. 比如说在你的子类实例中隐藏某些父类的属性或方法.下面的例子演示了实现一个没有append方法的list类:
- 这个类的实例几乎与内建list对象完全相同,除了性能更差以外. 任何对该类实例append方法的调用都会引发一个异常.
1 class AttributeWatcher:
2 def __init__(self):
3 # note the caution to avoid triggering __setattr__, and the
4 # emulation of Python's name-mangling for a private attribute
5 self.__dict__['_AttributeWatcher__mydict']={ }
6 def __getattr__(self, name):
7 # as well as tracing every call, for demonstration purposes we
8 # also fake "having" any requested attribute, EXCEPT special
9 # methods (__getattr__ is also invoked to ask for them: check by
10 # trying a few operations on an AttributeWatcher instance).
11 print "getattr", name
12 try: return self.__mydict[name]
13 except KeyError:
14 if name.startswith('__') and name.endswith('__'):
15 raise AttributeError, name
16 else: return 'fake_'+name
17 def __setattr__(self, name, value):
18 print "setattr", name, value
19 self.__mydict[name] = value
20 def __delattr__(self, name):
21 print "delattr", name
22 try: del self.__mydict[name]
23 except KeyError: pass
1.1.4.4. 5.2.4.4 Per-instance methods
个体实例方法
- 经典与新版对象模型都允许一个实例绑定私有的属性和方法. 对一个方法来讲,和其它属性一样,一个私有的属性绑定将覆盖类定义中的同名属性:只要在实例中发现有这个属性,就不再继续查找类定义了. 经典与新版对象模型的不同之处在于实例私有的特殊方法. 在经典对象模型中,一个实例可以有效的覆盖由类定义提供的特殊方法. 在新版对象模型中,实例特殊方法的隐式调用总是依赖类定义中的特殊方法而不是实例中新绑定的特殊方法.下面这个例子可以说明这一点:
1 def fakeGetItem(idx): return idx
2 class Classic: pass
3 c = Classic( )
4 c.__getitem__ = fakeGetItem
5 print c[23] # prints: 23
6 class NewStyle(object): pass
7 n = NewStyle( )
8 n.__getitem__ = fakeGetItem
9 print n[23] # results in:
10 # Traceback (most recent call last):
11 # File "<stdin>", line 1, in ?
12 # TypeError: unindexable object
可以看到,在调用n[23]时,这是一个隐式的getitem方法调用,因为NewStyle类中并未定义该方法,所以引发了异常 不过如果你使用n.getitem(23)这种方式来显式调用特殊方法时,它还是可以工作的
1.1.5. 5.2.5 新版对象模型中的继承
Inheritance in the New-Style Object Model
- 在新版对象模型中,继承的使用方式与传统模型大致相同.一个主要的区别是新版类可以从一个内建类型中继承. 新版的类仍然支持多继承,不过只有多个内建类型是特殊设计并允许相互兼容的情况下才可以被继承.通常情况,一个新版类只允许继承一个真实类型.这意味着在多继承时,除object以外,至多一个内建类型可以是所有内建类型及新版类的超类.
1.1.5.1. 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,不能通过实例.
1.1.5.2. 5.2.5.2 Cooperative superclass method calling
' 协作式调用超类方法'
- 前面我们提丟,当一个子类覆盖了一个方法,这个覆盖的方法通常要调用被覆盖的方法.python传统对象模型惯用的方式是用非绑定方法语法调用父类的同名方法.当多继承时,这种方法是有缺限的,见下例:
- 在上面的代码中,当我们调用 D().met()方法时, A.met()方法被调用了两次. 我们怎样才可以保证每个父类的实现均被顺序调用且仅仅调用一次呢?不采取点特殊措施这个问题很难解决.从python2.2起,提供了这样一个特殊手段. 那就是 super类型.super(aclass,obj)返回一个与obj相关的特殊对象. 当我们调用该对象的一个属性或方法时,这一段真不好翻译,总之,这样就保证了每个父类的实现均被调用且仅仅调用一次了. 改写后的代码如下:
- 如果你养成了总是使用superclass调用父类方法,你的类就能适应无论多复杂的继承结构.
