位置: 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怎么选中图形放大)

  • 智能收款云音箱怎么连接手机(智能收款云音箱怎么使用)

    智能收款云音箱怎么连接手机(智能收款云音箱怎么使用)

  • 抖音看了直播忘了关注怎么找到(抖音看了直播忘记关麦)

    抖音看了直播忘了关注怎么找到(抖音看了直播忘记关麦)

  • 小米盒子怎么用(小米盒子怎么用浏览器)

    小米盒子怎么用(小米盒子怎么用浏览器)

  • iphonexs什么时候出的(iphonexs什么时候开始卖)

    iphonexs什么时候出的(iphonexs什么时候开始卖)

  • 小米手机换机的时候怎么把内容换过去(小米换机用法)

    小米手机换机的时候怎么把内容换过去(小米换机用法)

  • 微信怎么删好友(微信怎么删好友还能保留聊天记录)

    微信怎么删好友(微信怎么删好友还能保留聊天记录)

  • 健康码刷新是什么意思(健康码刷新慢)

    健康码刷新是什么意思(健康码刷新慢)

  • 微信收款音箱S1和S2和S3有什么区别(微信收款音箱s1连不上网怎么办)

    微信收款音箱S1和S2和S3有什么区别(微信收款音箱s1连不上网怎么办)

  • 有线桥接两个路由器可以设置同名同密码吗(有线桥接两个路由器可以无缝连接)

    有线桥接两个路由器可以设置同名同密码吗(有线桥接两个路由器可以无缝连接)

  • 淘宝直播右下角的爱心是什么(淘宝直播右下角点赞有什么用处吗)

    淘宝直播右下角的爱心是什么(淘宝直播右下角点赞有什么用处吗)

  • 华为荣耀6plus怎么设置定时开关机(华为荣耀6plus怎么插卡)

    华为荣耀6plus怎么设置定时开关机(华为荣耀6plus怎么插卡)

  • 荣耀20是多少w快充(荣耀20是多少w快充数据线)

    荣耀20是多少w快充(荣耀20是多少w快充数据线)

  • 苹果手机可以换内存卡吗(苹果手机可以换铃声吗怎么换)

    苹果手机可以换内存卡吗(苹果手机可以换铃声吗怎么换)

  • ip67级防水多少米(ip67级防水和ipx7级防水)

    ip67级防水多少米(ip67级防水和ipx7级防水)

  • 手机拍照怎么小于2m(手机拍照怎么小于500k)

    手机拍照怎么小于2m(手机拍照怎么小于500k)

  • 微控制器的组成(微控制器内部都有哪些常用部件?)

    微控制器的组成(微控制器内部都有哪些常用部件?)

  • 怎么删除已发的抖音(怎么删除已发的微信朋友圈)

    怎么删除已发的抖音(怎么删除已发的微信朋友圈)

  • 关闭微信开机启动(微信怎样关闭开机启动)

    关闭微信开机启动(微信怎样关闭开机启动)

  • switch续航版和普通版区别

    switch续航版和普通版区别

  • 搜索别人的抖音别人知道吗(搜索别人的抖音号会被发现吗)

    搜索别人的抖音别人知道吗(搜索别人的抖音号会被发现吗)

  • 腾讯视频保存的图片在哪(腾讯视频保存的视频怎么到手机相册)

    腾讯视频保存的图片在哪(腾讯视频保存的视频怎么到手机相册)

  • 云闪付扫一扫在哪里(云闪付扫一扫付款后钱在哪里看)

    云闪付扫一扫在哪里(云闪付扫一扫付款后钱在哪里看)

  • 滴滴转人工服务按几(滴滴司机客服人工怎么转人工)

    滴滴转人工服务按几(滴滴司机客服人工怎么转人工)

  • 增值税专用发票可以开电子发票吗
  • 怎么做税种
  • 现金回收期计算公式
  • 一般纳税人公司抵扣
  • 科目余额表怎么导出
  • 开票地址太长显示不全
  • 采购的固定资产验收无误后直接安装使用
  • 办公室转租怎么给对方开发票
  • 农民工工资保证金管理暂行办法
  • 2019年小微企业六税两费减免
  • 预付房租在资产负债表里为什么属于资产
  • 新企业需要结转增值税吗
  • 以前年度损益调整影响哪个科目
  • 个体户办理税务登记有什么好处
  • 招待费的进项
  • 哪些是非累计带薪缺勤?
  • 那些发票可以报
  • 支付的各项税费包括个人所得税吗
  • 小规模季度不超过30万元免税会计分录
  • 小微企业可以抵税吗
  • 半年付一次房租,房东一般提前多长时间催租
  • 国税地税合并后叫什么名称
  • 利润分配可以用现金吗
  • 海关年检需要什么资料
  • 研发和技术服务在开票软件中怎么找
  • 样品赠送要确认收入吗
  • 所得税费用可以抵减利润吗
  • 银行存款利息的计算方法
  • 你肯定不知道的11个狗狗冷知识
  • 未开票收入缴纳所得税吗
  • 8款应用
  • 单位卖二手车需交税吗
  • php最新动态
  • 企业网银证书费用收费标准
  • 股息红利要交税吗
  • window显示我的电脑
  • phppoem
  • 朱顶雀的饲养
  • 预付卡销售可以报销吗
  • 汇算清缴退税分录怎么写
  • 报销差旅费属于什么现金流量项目
  • php数据库网址
  • 公司垫付的工伤医药费怎么报销
  • 公司处置已使用4年多的汽车,怎么增值税申报
  • mongodb建立索引的命令
  • 帝国cms如何使用
  • 新版电子税务局怎么增加办税人员
  • sql row_number()over()
  • 防洪费需要计提吗
  • 公司注销应付款如何办理
  • 开具红字发票如何做账?
  • 员工自己承担的社保费用进入管理费用吗
  • 利息收入的正确分录凭证怎么写
  • winxp和win7共享
  • xp window
  • windows2003服务
  • mac上dns设置
  • windows7光盘安装
  • 电脑出windows
  • svcinit.exe - svcinit是什么进程
  • incredr
  • win8怎么设置
  • win8.1 应用商店是不是不能用了
  • 铁嘴李林
  • cocos设置锚点
  • 编写折半查找的程序
  • 批处理清空文本内容
  • shell脚本怎么写循环
  • [置顶]星陨计划
  • 国家税务局总局官网
  • 诺诺开票软件怎么领用发票
  • 泌阳县国土资源局崔超
  • 丰田威驰2023款1.5自动挡新车报价
  • 个人的房产出租征收什么税
  • 七创社开放时间
  • 辽宁省财政局会计网
  • 交强险和车船税网上购买
  • 医疗发票真伪查询
  • 汉中税务大厅电话是多少
  • 应缴增值税
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设