python描述符descriptor(二)

python内置的描述符

python有些内置的描述符对象,property、staticmethod、classmethod,python实现如下:

class Property(object):

    def __init__(self,getf,setf,delf,doc):

        self.getf=getf

        self.setf=setf

        self.delf=delf

        self.doc=doc

    def __get__(self,instance,own=None):

        if instance is None:

            return self

        if  self.getf is None:

            raise AttributeError

        return self.getf(instance)

    def __set__(self,instance,value):

        if self.setf is None:

            raise AttributeError

        self.setf(instance,value)

    def __del__(self,instance):

        if self.delf is None:

            raise AttributeError    

        self.delf(instance)

class StaticMethod(object):

    def __init__(self,func):

        self.func=func

    def __get__(self,instance,own=None):

        return self.func

class ClassMethod(object):

    def __init__(self,func):

        self.func=func

    def __get__(self,instance,own=None):

        if own is None:

            own=type(instance)

        def callfunc(*args):

            return self.func(own,*args)

        return callfunc

为属性值设置别名

有时候你想用一个属性名作为另一个属性名的别名,比如设置一些属性的默认值必须和其他属性的当前值一样,而且还需要独立的设置和删除。

class DefaultAlias(object):

    def __init__(self,name):

        self.name=name

    def __get__(self,instance,own):

        if instance is None:  #类属性访问时

            return self

        return getattr(instance,self.name).title()

class Person(object):

    def __init__(self,name,aliasname=None):

        self.name=name

        if aliasname is not None:

            self.aliasname=aliasname

    aliasname=DefaultAlias('name')

>>> p=Person('sam')

>>> p.aliasname

'Sam'

>>> p.aliasname='jack'

>>> p.aliasname

'jack'

>>> del p.aliasname

>>> p.aliasname

'Sam'

这样就为属性name设置了一个别名aliasname,或者说把aliasname的值存储在了name中。DefaultAlias并不是数据描述符,因为它没有__set__方法,而是一个non-data描述符。所以我们给一个实例属性赋值时(p.aliasname='jack'),实例会正常地记录属性,而且实例属性会覆盖掉类属性。这样aliasname属性就能单独的设置而不影响name属性了。当我们del p.aliasname时,删除了实例的属性,类属性又会再次显现出来。

对于某些开发的类,如果要保持后续版本的兼容性,可以用新名称来命名方法和属性,同时保留旧名字的可用性。

class OldAlias(object):

    def __init__(self,name,oldname):

        self.name=name

        self.oldname=oldname

    def _warn(self):

        print 'use %r,not %r'%(self.name,self.oldname)

    def __get__(self,instance,own):

        self._warn()

        if instance is None:  

            return self

        return getattr(instance,self.name)

    def __set__(self,instance,value):

        self._warn()

        setattr(instance,self.name,value)

    def __del__(self,instance):

        self._warn()

        delattr(instance,self.name)

class NewClass(object):

    def __init__(self,newname):

        self.newname=newname

    oldname=OldAlias('newname','oldname')
>>> c=NewClass('a')

>>> c.oldname

use 'newname',not 'oldname'

'a'

使用这个类的旧代码会使用类属性oldname,同时一个警告信息被打印,鼓励用户使用新属性newname。

缓存属性值

根据需求计算实例属性或类属性的值,并提供自动化的缓存。

class CachedAttribute(object):

    def __init__(self,method,name=None):

        self.method=method

        self.name=name if name else method.__name__

    def __get__(self,instance,own):

        if instance is None:

            return self

        result=self.method(instance)

        setattr(instance,self.name,result)

        return result

class MyObject(object):

    def __init__(self,n):

        self.n=n

    @CachedAttribute

    def square(self):

        return self.n*self.n

>>> m=MyObject(2)

>>> m.square

4

>>> m.n=5

>>> m.square

4

>>> del m.square

>>> m.square

25

在首次访问m.square后,square属性就被缓存在实例m中,当改变实例属性n时,square属性不会改变。如果需要清除缓存,del m.square即可,再次访问m.square属性square的值会被再次计算。

缓存类属性:

class CachedClassAttribute(CachedAttribute):

    def __get__(self,instance,own):

        return super(CachedClassAttribute,self).__get__(own,own)

class MyClass(object):

    class_attr=24

    @CachedClassAttribute

    def square(cls):

        return cls.class_attr*cls.class_attr

这样类的所有实例都有同样的缓存值了:

>>> a=MyClass()

>>> b=MyClass()

>>> a.square

>>> print a.square

576

>>> print b.square

576

>>> print MyClass.square

576

  

你可能感兴趣的:(python)