位置: IT常识 - 正文

Python中的描述符(python描述器有什么用)

编辑:rootadmin

推荐整理分享Python中的描述符(python描述器有什么用),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:python描述算法的方法有几种,python描述器有什么用,python描述算法的方法有几种,python描述性统计代码,python描述性统计代码,python描述性统计代码,python描述算法的方法有几种,python描述器有什么用,内容如对您有帮助,希望把文章链接给更多的朋友!

描述符是一种在多个属性上重复利用同一个存取逻辑的方式,他能"劫持"那些本对于self.__dict__的操作。描述符通常是一种包含__get__、__set__、__delete__三种方法中至少一种的类,给人的感觉是「把一个类的操作托付与另外一个类」。静态方法、类方法、property都是构建描述符的类。

我们先看一个简单的描述符的例子:

classMyDescriptor(object):_value=''def__get__(self,instance,klass):returnself._valuedef__set__(self,instance,value):self._value=value.swapcase()classSwap(object):swap=MyDescriptor()

注意MyDescriptor要用新式类。调用一下:

In[1]:fromdescriptor_exampleimportSwapIn[2]:instance=Swap()In[3]:instance.swap#没有报AttributeError错误,因为对swap的属性访问被描述符类重载了Out[3]:''In[4]:instance.swap='makeitswap'#使用__set__重新设置_valueIn[5]:instance.swapOut[5]:'MAKEITSWAP'In[6]:instance.__dict__#没有用到__dict__:被劫持了Out[6]:{}

这就是描述符的威力。我们熟知的staticmethod、classmethod如果你不理解,那么看一下用Python实现的效果可能会更清楚了:

>>>classmyStaticMethod(object):...def__init__(self,method):...self.staticmethod=method...def__get__(self,object,type=None):...returnself.staticmethod...>>>classmyClassMethod(object):...def__init__(self,method):...self.classmethod=method...def__get__(self,object,klass=None):...ifklassisNone:...klass=type(object)...defnewfunc(*args):...returnself.classmethod(klass,*args)...returnnewfunc

在实际的生产项目中,描述符有什么用处呢?首先看MongoEngine中的Field的用法:

frommongoengineimport*classMetadata(EmbeddedDocument):tags=ListField(StringField())revisions=ListField(IntField())classWikiPage(Document):title=StringField(required=True)text=StringField()metadata=EmbeddedDocumentField(Metadata)

有非常多的Field类型,其实它们的基类就是一个描述符,我简化下,大家看看实现的原理:

classBaseField(object):name=Nonedef__init__(self,**kwargs):self.__dict__.update(kwargs)...def__get__(self,instance,owner):returninstance._data.get(self.name)def__set__(self,instance,value):...instance._data[self.name]=value

很多项目的源代码看起来很复杂,在抽丝剥茧之后,其实原理非常简单,复杂的是业务逻辑。

Python中的描述符(python描述器有什么用)

接着我们再看Flask的依赖Werkzeug中的cached_property:

class_Missing(object):def__repr__(self):return'novalue'def__reduce__(self):return'_missing'_missing=_Missing()classcached_property(property):def__init__(self,func,name=None,doc=None):self.__name__=nameorfunc.__name__self.__module__=func.__module__self.__doc__=docorfunc.__doc__self.func=funcdef__set__(self,obj,value):obj.__dict__[self.__name__]=valuedef__get__(self,obj,type=None):ifobjisNone:returnselfvalue=obj.__dict__.get(self.__name__,_missing)ifvalueis_missing:value=self.func(obj)obj.__dict__[self.__name__]=valuereturnvalue

其实看类的名字就知道这是缓存属性的,看不懂没关系,用一下:

classFoo(object):@cached_propertydeffoo(self):print'Callme!'return42

调用下:

In[1]:fromcached_propertyimportFoo...:foo=Foo()...:In[2]:foo.barCallme!Out[2]:42In[3]:foo.barOut[3]:42

可以看到在从第二次调用bar方法开始,其实用的是缓存的结果,并没有真的去执行。

说了这么多描述符的用法。我们写一个做字段验证的描述符:

classQuantity(object):def__init__(self,name):self.name=namedef__set__(self,instance,value):ifvalue>0:instance.__dict__[self.name]=valueelse:raiseValueError('valuemustbe>0')classRectangle(object):height=Quantity('height')width=Quantity('width')def__init__(self,height,width):self.height=heightself.width=width@propertydefarea(self):returnself.height*self.width

我们试一试:

In[1]:fromrectangleimportRectangleIn[2]:r=Rectangle(10,20)In[3]:r.areaOut[3]:200In[4]:r=Rectangle(-1,20)---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)<ipython-input-5-5a7fc56e8a>in<module>()---->1r=Rectangle(-1,20)/Users/dongweiming/mp/2017-03-23/rectangle.pyin__init__(self,height,width)1516def__init__(self,height,width):--->17self.height=height18self.width=width19/Users/dongweiming/mp/2017-03-23/rectangle.pyin__set__(self,instance,value)7instance.__dict__[self.name]=value8else:---->9raiseValueError('valuemustbe>0')1011ValueError:valuemustbe>0

看到了吧,我们在描述符的类里面对传值进行了验证。ORM就是这么玩的!

但是上面的这个实现有个缺点,就是不太自动化,你看height = Quantity('height'),这得让属性和Quantity的name都叫做height,那么可不可以不用指定name呢?当然可以,不过实现的要复杂很多:

classQuantity(object):__counter=0def__init__(self):cls=self.__class__prefix=cls.__name__index=cls.__counterself.name='_{}#{}'.format(prefix,index)cls.__counter+=1def__get__(self,instance,owner):ifinstanceisNone:returnselfreturngetattr(instance,self.name)...classRectangle(object):height=Quantity()width=Quantity()...

Quantity的name相当于类名+计时器,这个计时器每调用一次就叠加1,用此区分。有一点值得提一提,在__get__中的:

ifinstanceisNone:returnself

在很多地方可见,比如之前提到的MongoEngine中的BaseField。这是由于直接调用Rectangle.height这样的属性时候会报AttributeError, 因为描述符是实例上的属性。

PS:这个灵感来自《Fluent Python》,书中还有一个我认为设计非常好的例子。就是当要验证的内容种类很多的时候,如何更好地扩展的问题。现在假设我们除了验证传入的值要大于0,还得验证不能为空和必须是数字(当然三种验证在一个方法中验证也是可以接受的,我这里就是个演示),我们先写一个abc的基类:

classValidated(abc.ABC):__counter=0def__init__(self):cls=self.__class__prefix=cls.__name__index=cls.__counterself.name='_{}#{}'.format(prefix,index)cls.__counter+=1def__get__(self,instance,owner):ifinstanceisNone:returnselfelse:returngetattr(instance,self.name)def__set__(self,instance,value):value=self.validate(instance,value)setattr(instance,self.name,value)@abc.abstractmethoddefvalidate(self,instance,value):"""returnvalidatedvalueorraiseValueError"""

现在新加一个检查类型,新增一个继承了Validated的、包含检查的validate方法的类就可以了:

classQuantity(Validated):defvalidate(self,instance,value):ifvalue<=0:raiseValueError('valuemustbe>0')returnvalueclassNonBlank(Validated):defvalidate(self,instance,value):value=value.strip()iflen(value)==0:raiseValueError('valuecannotbeemptyorblank')returnvaluedefquantity():try:quantity.counter+=1exceptAttributeError:quantity.counter=0storage_name='_{}:{}'.format('quantity',quantity.counter)defqty_getter(instance):returngetattr(instance,storage_name)defqty_setter(instance,value):ifvalue>0:setattr(instance,storage_name,value)else:raiseValueError('valuemustbe>0')returnproperty(qty_getter,qty_setter)
本文链接地址:https://www.jiuchutong.com/zhishi/303122.html 转载请保留说明!

上一篇:详解Python元类(metaclass)(python 元类 详解)

下一篇:ps怎么选中图形(ps怎么选中图形放大)

  • iqooz3是不是5g手机(iqooz3有5g吗)

    iqooz3是不是5g手机(iqooz3有5g吗)

  • 微信接不到语音通话和视频是怎么回事呢(微信接不到语音电话 过后只能收到未接提醒)

    微信接不到语音通话和视频是怎么回事呢(微信接不到语音电话 过后只能收到未接提醒)

  • 先体验后付款不满意怎么办(先体验后付款怎么还款)

    先体验后付款不满意怎么办(先体验后付款怎么还款)

  • 为什么qq修改实名认证不符合条件(为什么qq修改实名认证老是信息不符)

    为什么qq修改实名认证不符合条件(为什么qq修改实名认证老是信息不符)

  • 物联网卡正确使用方法(物联网卡使用方法)

    物联网卡正确使用方法(物联网卡使用方法)

  • minidp接口可以接144hz显示器吗(minidp接口可以接显示器嘛)

    minidp接口可以接144hz显示器吗(minidp接口可以接显示器嘛)

  • x50pro是什么牌子(x50 pro是什么手机)

    x50pro是什么牌子(x50 pro是什么手机)

  • qq冻结好友辅助多久通过(qq好友辅助验证一直不通过)

    qq冻结好友辅助多久通过(qq好友辅助验证一直不通过)

  • 如何显示没有下划线的超链接(如何显示没有下划线的超链接正确答案是)

    如何显示没有下划线的超链接(如何显示没有下划线的超链接正确答案是)

  • 美团买单申请多久出结果(美团 买单)

    美团买单申请多久出结果(美团 买单)

  • oppo手表能连苹果手机吗(oppo手表能不能连苹果手机)

    oppo手表能连苹果手机吗(oppo手表能不能连苹果手机)

  • 情侣空间点不进去怎么回事(情侣空间点不进去图片)

    情侣空间点不进去怎么回事(情侣空间点不进去图片)

  • 手机必须充满才能拔吗(手机一定充满才能拔掉吗)

    手机必须充满才能拔吗(手机一定充满才能拔掉吗)

  • hwt文件主题怎么用(hwt主题文件怎么打开不进去)

    hwt文件主题怎么用(hwt主题文件怎么打开不进去)

  • 微信语音静音对方显示(微信语音静音对方会显示静音吗)

    微信语音静音对方显示(微信语音静音对方会显示静音吗)

  • word文字怎么画横道图(word文字怎么画斜线)

    word文字怎么画横道图(word文字怎么画斜线)

  • 运算器由哪些部分组成(运算器包括哪些部件)

    运算器由哪些部分组成(运算器包括哪些部件)

  • 美团消费账单怎么删除(美团消费账单在哪里看)

    美团消费账单怎么删除(美团消费账单在哪里看)

  • wps表格怎么删一行(wps表格怎么删一列内容)

    wps表格怎么删一行(wps表格怎么删一列内容)

  • vivo怎么看销毁的闪照(vivo手机怎么看拆没拆过机)

    vivo怎么看销毁的闪照(vivo手机怎么看拆没拆过机)

  • 快手收藏的视频在哪里(快手收藏的视频怎么删除)

    快手收藏的视频在哪里(快手收藏的视频怎么删除)

  • 抖音抢镜怎么变圆形(抖音抢镜在哪里抢镜怎么缩小)

    抖音抢镜怎么变圆形(抖音抢镜在哪里抢镜怎么缩小)

  • 微信图标有个蓝色对勾(微信图标有个蓝色)

    微信图标有个蓝色对勾(微信图标有个蓝色)

  • 通过电话定位(通过电话定位找人免费)

    通过电话定位(通过电话定位找人免费)

  • U盘中病毒了怎么办(u盘中病毒了怎么格式化)

    U盘中病毒了怎么办(u盘中病毒了怎么格式化)

  • pppsetup命令  设置PPP连线(setup prompt)

    pppsetup命令 设置PPP连线(setup prompt)

  • 异地预缴可以退吗
  • 增值税发票超过3个月可以作废吗
  • 撤回或减少投资同撤资减资的区别
  • 一般纳税人购销印花税减半吗
  • 农业技术服务个人总结
  • 个税缴款三方协议
  • 房地产企业如何进行市场细分
  • 企业资产损失所得税税前扣除办法
  • 物业公司需要向哪个部门缴费
  • 两个单位同一个法人
  • 代持股 税收
  • 到期一次还本付息债券
  • 未在规定期限内发出追索通知
  • 有限公司资本公积转增股本要交税吗
  • 提回对公户收款是什么意思
  • 个人所得税工资薪金税率表
  • 汇算清缴做完还能改账么
  • 高新技术企业的行业
  • 企业所得税减免税额计算公式
  • 子网掩码和默认网关怎么填
  • win10系统如何注册dll文件
  • 税金及附加怎么登明细账
  • redis在php项目中的使用实例
  • 微博 照片水印
  • 贷款利息不能抵扣进项税
  • PHP:pg_escape_literal()的用法_PostgreSQL函数
  • 应付债券的应付利息怎么计算
  • 增值税发票开红字发票后账务处理?
  • 未确认融资费用报表填在哪个科目
  • 不抵扣的进项税怎么做账
  • Android Studio安装和使用教程(全文图解)
  • 当年实现的利润弥补以前年度亏损还是提盈余公积
  • 从零开始作者
  • jdbc连接mysql数据库不成功
  • 波士顿房价数据集可视化
  • php 输出
  • lastlog日志
  • 银行存放中央银行准备金的会计处理
  • jsp标签大全
  • 应交税费-应交增值税
  • 跨年的发票可以退税吗
  • mysql内连接查询使用汉语作为官方语言的所有国家
  • 计提怎么理解
  • sqlserver2005没有服务器名称
  • 收到发票并支付货款怎么做账
  • 陈列费计入什么科目
  • 固定资产低于净值出售会计处理
  • 在建工程如何结转到产品
  • 4s店销售返利
  • 坏账准备转回并收到货款会计分录
  • 固定资产大修理支出摊销年限
  • 对外投资的会计科目
  • 青苗赔偿管理办法
  • 餐饮费与业务招标的关系
  • 商业成本会计如何记账
  • 小规模纳税人是开专票还是普票
  • mysql column is ambiguous
  • 微信昵称带符号图案
  • win10怎么进u盘系统
  • ubuntu zmq
  • /wlan direct
  • 因为你的策略组阻止
  • mac上怎么用ppt
  • win7打开
  • win8系统切换桌面
  • cocos2dx4.0教程
  • 批处理文件命令大全
  • css div模糊
  • 如何获取硬盘所有文件的列表
  • python pygame模块详解
  • jquery示例
  • node.js基础入门
  • python操作db2数据库
  • JavaScript快速排序
  • 财政票据和税务票据的法律效力一样吗
  • 江苏囯税电子
  • 增值税开票系统升级
  • 资源税百科
  • 蓬溪房价2020最新消息
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设