python描述符descriptor(二)
python内置的描述符
python有些内置的描述符对象,property、staticmethod、classmethod,python实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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 |
为属性值设置别名
有时候你想用一个属性名作为另一个属性名的别名,比如设置一些属性的默认值必须和其他属性的当前值一样,而且还需要独立的设置和删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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' ) |
1 2 3 4 5 6 7 8 9 | >>> 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时,删除了实例的属性,类属性又会再次显现出来。
对于某些开发的类,如果要保持后续版本的兼容性,可以用新名称来命名方法和属性,同时保留旧名字的可用性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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' ) |
1 2 3 4 | >>> c = NewClass( 'a' ) >>> c.oldname use 'newname' , not 'oldname' 'a' |
使用这个类的旧代码会使用类属性oldname,同时一个警告信息被打印,鼓励用户使用新属性newname。
缓存属性值
根据需求计算实例属性或类属性的值,并提供自动化的缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
1 2 3 4 5 6 7 8 9 | >>> 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的值会被再次计算。
缓存类属性:
1 2 3 4 5 6 7 8 | 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 |
这样类的所有实例都有同样的缓存值了:
1 2 3 4 5 6 7 8 9 | >>> a = MyClass() >>> b = MyClass() >>> a.square >>> print a.square 576 >>> print b.square 576 >>> print MyClass.square 576 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探