面向对象的python(二)
huaihe0410
|
1#
huaihe0410 发表于 2008-11-26 16:54
面向对象的python(二)
开始学习python的时候,看了一些教程和资料,都觉得在面向对象编程这一方面讲得比较零散,自己也就总觉得不得要领。直到看到了Oreilly出的Python in the Nutshell,英文版,特别是Charpter5: Object-Oriented Python,才开始明白一点点东西。这本书,对章节的编排非常合理,而且不光教你how还教你why,觉得受益匪浅。
看的过程中,自己陆续的记下一些东西,有对书的部分翻译,有自己的体会和测试代码。 翻译中,有少部分是直接翻译,大部分其实只是自己的意译,还添油加醋加上了自己的一些说明。毕竟,我的目的是为了把问题弄懂,不想去做那么多hard translation反让人如坠迷雾。于是想汇成一篇,就有了这篇文章。文章的安排,基本上和Python in the Nutshell的Charpter5相同,但内容要短得多。 Wilson Sun ---------------------------------------------------------------------------------------------------------------- 1.1 新型对象(New-Style Classes) 在python的2.2和2.3版中,若对象直接或间接地继承了python的内建类型的对象,那么它就是新型对象。 “经典”往往是对旧事物的尊称,既然从2.2版开始推出新型对象,肯定是有种种好处的,所以该尽量用新型对象。 1.1.1 内建类型:object 从python的2.2开始,object是一种内建类型,它也是所有其它内建类型和新型对象的超类。 继承object的对象,需要重载下面方法: _ _new_ _ _ _init_ _ _ _delattr_ _ _ _getattribute_ _ _ _setattr_ _ _ _hash_ _ _ _repr_ _ _ _str_ _ 1.1.2 类级方法(Class-Level Methods) 类级方法是新型对象的特征之一,有两类:静态方法和对象方法。 1.1.2.1 静态方法(Static methods) 静态方法不存在bound和unbound的问题,可以直接调用。用staticmethod声明一个静态方法,如: class AClass(object): def astatic( ): print 'a static method' astatic = staticmethod(astatic) anInstance = AClass( ) AClass.astatic( ) 1.1.2.2 类方法(Class methods) 可以在类的内部和类的实例上调用类方法。用classmethod声明,如: class ABase(object): def aclassmet(cls): print 'a class method for', cls._ _name_ _aclassmet = classmethod(aclassmet)class ADeriv(ABase): passbInstance = ABase( )dInstance = ADeriv( )ABase.aclassmet( ) # prints: a class method for ABasebInstance.aclassmet( ) # prints: a class method for ABaseADeriv.aclassmet( ) # prints: a class method for ADerivdInstance.aclassmet( ) # prints: a class method for ADeriv 类方法的第一个参数,就是调用这个方法的对象。 1.1.3 新型对象 新型对象也具有所有经典对象的特征,但它们还有更为独特的特征,_ _init_ _和_ _new_ _ 1.1.3.1 _ _init_ _ 一个新型对象C,它会直接或间接地继承object中的_ _init_ _方法,如果你不在C当中重载_ _init_ _方法的话,你传递给C的_ _init_ _方法任何参数都会被python忽略。 为了避免乱传参数,建议:即使不想在_ _init_ _做任何事,也该重载_ _init_ _方法,如: class C(object): def __init__(self): pass 这样,如果错误地向_ _init_ _传递了参数,python就会抛出一个异常。 1.1.3.2 _ _ new_ _ 所有新型对象都有一个静态方法:_ _new_ _。 假设有一个新型对象C,现在你要创建C的一个实例x,那么你会写: x = C(23) 执行过程是:python首先调用C中的_ _new_ _方法,_ _new_ _会返回一个实例,若检测过后该实例的确是C的实例,那么_ _init_ _会被调用,若_ _new_ _返回的实例不是C的实例,那么这个实例将不会被初始化(_ _init_ _)。这一过程和下面的代码等价: x = C._ _new_ _(C, 23) if isinstance(x, C): C._ _init_ _(x, 23) 你也可以自己在C的内部重载它的_ _new_ _方法,这种重载不需要用staticmethod来声明。 _ _new_ _方法可以用来返回不同的实例,可以用这种方式来实现Factory工厂模式,还可以实现singleton单态模式。 1.1.4 新型对象的实例 经典对象的实例中的所有特性,在新型对象的实例中也同样具备,新型对象实例还具备一些与之不同的特性。 1.1.4.1 属性(properties) 这里说的“属性”,是和方法相对的。在对象内部定义属性的时候,用python的内建类型:property。示例如下: 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') 上例中,area就是Rectangle的属性,由于只定义了get方法,所以它是只读属性。doc是属性的docstring参数,docstring的作用非常类似于注释,但和注释相比,它可以在运行时被使用,这时它的一大优点。 使用property的语法如下: attrib = property(fget=None, fset=None, fdel=None, doc=None) 属性操作 代码示例 Python的实现 读取 n = x.attrib 返回property中fget函数值 赋值 x.attrib = 54 将值传入perperty中fset函数 删除 del x.attrib 调用fdel函数 在经典对象模型中,如果要实现上面的代码,要这样写: 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 1.1.4.2 _ _slots_ _ _ _slots_ _的作用和_ _dict_ _一样,用来存放实例的所有方法和属性。_ _dict_ _是字典(dict)类型,而_ _slots_ _是元组(tuple)类型,所以用_ _slots_ _会更节约内存,这也是使用它的唯一目的。要注意的是,一旦使用_ _slots_ _,那么原先的_ _dict_ _就没有作用了。 如果一个对象会同时产生百万级数目的实例,用_ _slots_ _会起到节约内存的目的,如果只是几千个实例,那么没必要。 还需要注意的是:实例中的_ _slots_ _不会被子类继承。 示例: class OptimizedRectangle(Rectangle): _ _slots_ _ = 'width', 'heigth' 1.1.4.3 _ _getattribute_ _ 对实例属性的引用,都通过object中_ _getattribute_ _方法来实现。你也可以重载该方法来获得某些特殊的效果。例如,你不想list中的append方法被调用,就可以这样: class listNoAppend(list): def _ _getattribute_ _(self, name): if name = = 'append': raise AttributeError, name return list._ _getattribute_ _(self, name) 那么,每当x.append被调用的时候,都会出现异常。 1.1.4.4 Per-instance methods 1.1.5 新型对象中的继承 在继承这一方面,和经典对象相比,新型对象最大区别就是可以继承自内建类型的对象,而且python中是允许多重继承的。 1.1.5.1 方法的解析顺序 当存在继承关系的时候,python会到一个对象的基类(base class)中去查找方法或属性,特别是当存在多重继承关系的时候,查找的顺序是怎样的呢?这种查找顺序,被称为解析顺序(resolution order)。 假设有类A,它直接继承自B和C(顺序为:B,C),B和C又都继承自D。 经典对象模型和新型模型对象的解析顺序为下图所示: 经典对象模型的解析顺序是:左边优先,再深度优先。所以它的解析顺序是:先A-B-D-C-D。 新型对象模型的即席顺序是:A-B-C-D-object 写程序的时候,经典对象模型的这种解析顺序可能会产生一些问题,所以新型对象中更改了解析顺序,会先解析同级的基类。 1.1.5.2 超类调用 当重载(override)一个方法的时候,我们往往会对超类的同名方法做一些操作,但在多重继承的情况下,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=D() d.met() 代码执行结果如下: D.met B.met A.met C.met A.met 可以看到,A中的met被调用了两次。如果才能保证超类中的同名方法只被调用一次呢?这可以用python2.2中的super来解决,super是一种新的内建类型。调用super(aclass,obj)会返回obj的超类。 上面的代码可以做如下的修改: 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( ) 注:tuple这个词,看到有多种翻译。觉得MS的Sql Server 2000联机手册中,把它译为“元组”,于是自己都采用这种翻译。 |