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

  • 浅谈旅游景点的微博营销3大策略(浅谈旅游景点的营销策略)

    浅谈旅游景点的微博营销3大策略(浅谈旅游景点的营销策略)

  • 哔哩哔哩一个硬币相当于多少元(哔哩哔哩一个硬币等于多少经验)

    哔哩哔哩一个硬币相当于多少元(哔哩哔哩一个硬币等于多少经验)

  • Win10的最低配置是什么(win10的最低配置cpu)

    Win10的最低配置是什么(win10的最低配置cpu)

  • ntc是什么电子元件

    ntc是什么电子元件

  • 金税盘插上电脑没反应(金税盘插上电脑上应该有什么显示)

    金税盘插上电脑没反应(金税盘插上电脑上应该有什么显示)

  • 苹果xs闪光灯怎么开(苹果xs闪光灯怎么关)

    苹果xs闪光灯怎么开(苹果xs闪光灯怎么关)

  • 笔记本intel可以撕掉吗(intel笔记本怎么样)

    笔记本intel可以撕掉吗(intel笔记本怎么样)

  • 华为手机怎么测网速(华为手机怎么测量身高)

    华为手机怎么测网速(华为手机怎么测量身高)

  • 苹果ld锁定是什么意思(苹果ld锁定什么意思)

    苹果ld锁定是什么意思(苹果ld锁定什么意思)

  • 手机小白点怎么关(手机小白点怎么打开)

    手机小白点怎么关(手机小白点怎么打开)

  • 联想笔记本键盘锁住了(联想笔记本键盘灯怎么开)

    联想笔记本键盘锁住了(联想笔记本键盘灯怎么开)

  • 如何查看微信中的历史广告(如何查看微信中对方撤回的消息记录)

    如何查看微信中的历史广告(如何查看微信中对方撤回的消息记录)

  • 华为屏幕录制不见了(华为屏幕录制不开麦克风)

    华为屏幕录制不见了(华为屏幕录制不开麦克风)

  • 爱奇艺显示互动播放是什么意思(爱奇艺显示互动加载失败)

    爱奇艺显示互动播放是什么意思(爱奇艺显示互动加载失败)

  • 高通msm8490八核是什么处理器

    高通msm8490八核是什么处理器

  • 怎么往电视上装软件(如何往电视上装软件)

    怎么往电视上装软件(如何往电视上装软件)

  • 华为充电器叫什么(华为充电器叫什么type c怎么读)

    华为充电器叫什么(华为充电器叫什么type c怎么读)

  • 华为手机怎么倒计天数(华为手机怎么倒东西到苹果手机)

    华为手机怎么倒计天数(华为手机怎么倒东西到苹果手机)

  • 苹果11的处理器是多少(苹果11的处理器是多大的)

    苹果11的处理器是多少(苹果11的处理器是多大的)

  • 手机qq如何送礼物(手机qq怎么送礼物)

    手机qq如何送礼物(手机qq怎么送礼物)

  • 苹果回收站在哪里(今日苹果回收价格表)

    苹果回收站在哪里(今日苹果回收价格表)

  • oppoa9什么时候上市的(0pp0a9什么时候上市)

    oppoa9什么时候上市的(0pp0a9什么时候上市)

  • 程控电话是谁发明的(程控电话之前是什么电话)

    程控电话是谁发明的(程控电话之前是什么电话)

  • iPhone Xs Max的颜色有几种(苹果xs max手机颜色)

    iPhone Xs Max的颜色有几种(苹果xs max手机颜色)

  • Win7旗舰版如何查看本机的Mac地址?(win7旗舰版如何恢复出厂设置)

    Win7旗舰版如何查看本机的Mac地址?(win7旗舰版如何恢复出厂设置)

  • 按月支付劳务费开发票
  • 什么是税务代理人
  • 综合保税区是什么意思,举个例子
  • 公司购买手机如何入账
  • 交易性金融资产的账务处理
  • 知道含税价,税率,怎么算税金
  • 企业所得税逾期了一定要去税局前台申报吗
  • 企业合并的相关税费计入哪里
  • 销售退回的增值税怎么处理
  • 意外死亡公司赔偿多少标准
  • 建筑业预征缴纳税款
  • 重新刻发票章需要什么资料
  • 营业执照许可经营信息怎么填
  • 关注!这两种情况下可免征增值税!
  • 业务招揽方式
  • 资产处置收益的含义
  • 财政给企业的补助资金企业是否需要开发票
  • 企业投资股票取什么名字
  • 三栏式明细账目录填写范本
  • 企业党建活动经费管理办法的规定
  • php的file函数
  • 股份支付的确认和计量原则
  • php解析json对象
  • php取二维数组的一组内容
  • 因质量问题收取停工通知
  • 代理业务收入包括哪些
  • 黄喉蜂虎鸟是保护动物吗
  • 仙岩寺庙
  • 动态模型图如何制作
  • vue3项目中使用el-dialog
  • mysql的排序规则
  • 学前端的可以是什么专业的
  • id命令的哪个参数可显示用户账号的uid信息
  • 《中华人民共和国民法典》
  • 微擎框架安装教程
  • 融资租赁首付租金会计账务处理
  • 个体工商户生产经营所得税税率表2023
  • 个人独资企业用企业资金进行个人消费
  • 其他资本公积主要包括
  • 专项储备期末有余额吗
  • 职工薪酬属于收益性支出
  • 如何访问一台网络上的电脑
  • 实收资本为零的风险 债权人
  • 买车进项税
  • 退货给谁
  • 固定资产的期末余额反映固定资产原值的结余额
  • 记账凭证摘要的填写要求有
  • 公司二手车销售
  • 房地产企业所得税税负率是多少
  • 小微企业营业外收入二级科目怎么
  • 现金短缺无法查明计入什么科目
  • 城镇土地使用税征收范围
  • 转账支票怎么填写会计凭证
  • 人力资源公司代发工资可以开专用发票吗
  • 本月无收入还用结转吗
  • 如何调整银行存款余额
  • 会计写数字的技巧和方法
  • 季度申报表弥补以前年度亏损
  • mysql存储过程菜鸟教程
  • win8.1应用
  • windows xp 注册表故障恢复
  • Win7怎么把桌面文件迁移到D盘中
  • SUSE Linux Enterprise Server 11 SP1 安装教程图文详解
  • xp系统安装版合集
  • win8系统怎么直接进入桌面
  • linux一键安装软件
  • linux apache怎么启动
  • cocos2d开发app
  • mac安装android
  • js继承怎么实现
  • jquery 滑块
  • jQuery中的AjaxSubmit使用讲解
  • python安装后怎么用
  • js如何封装
  • 整理Javascript事件响应学习笔记
  • 辽宁省国家税务局官网
  • 青海的医疗保险费是多少钱
  • 国家税务总局关于涉税中介
  • 2020年税务系统关闭
  • 房屋契税交错了能退吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设