面向对象的python(二)

类别:软件工程 点击:0 评论:0 推荐:

开始学习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): 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

类方法的第一个参数,就是调用这个方法的对象。

 

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联机手册中,把它译为“元组”,于是自己都采用这种翻译。

 

本文地址:http://com.8s8s.com/it/it33099.htm