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