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.funcclass 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.oldnameuse '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 resultclass MyObject(object): def __init__(self,n): self.n=n @CachedAttribute def square(self): return self.n*self.n
>>> m=MyObject(2)>>> m.square4>>> m.n=5>>> m.square4>>> del m.square>>> m.square25
在首次訪問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.square576>>> print b.square576>>> print MyClass.square576
新聞熱點
疑難解答