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

  • 这5个步骤,帮你提高运营的转化率(如何帮助他们)

    这5个步骤,帮你提高运营的转化率(如何帮助他们)

  • iqoo8pro怎么开启悬浮球(iQOO8Pro怎么开启root权限)

    iqoo8pro怎么开启悬浮球(iQOO8Pro怎么开启root权限)

  • 电脑登录的微信怎么发朋友圈(怎么下线电脑登录的微信)

    电脑登录的微信怎么发朋友圈(怎么下线电脑登录的微信)

  • 苹果11按键音如何关闭(苹果按键音如何设置)

    苹果11按键音如何关闭(苹果按键音如何设置)

  • 华为畅玩9a的上市时间(华为畅玩9a到底好不好)

    华为畅玩9a的上市时间(华为畅玩9a到底好不好)

  • 小米9se屏幕乱跳是怎么回事(小米9se屏幕乱跳是什么原因)

    小米9se屏幕乱跳是怎么回事(小米9se屏幕乱跳是什么原因)

  • oppoa 5手机怎么投屏(oppoa5手机怎么设置返回键)

    oppoa 5手机怎么投屏(oppoa5手机怎么设置返回键)

  • 曲屏钢化膜怎么贴不住(曲屏钢化膜怎么贴视频)

    曲屏钢化膜怎么贴不住(曲屏钢化膜怎么贴视频)

  • 硬盘使用4000多小时算新吗(硬盘使用4000多次)

    硬盘使用4000多小时算新吗(硬盘使用4000多次)

  • 电信大圣卡怎么激活(电信大圣卡怎么升级一元一G)

    电信大圣卡怎么激活(电信大圣卡怎么升级一元一G)

  • 录音文件发微信怎么发(录音文件发微信安全吗)

    录音文件发微信怎么发(录音文件发微信安全吗)

  • 服务器关闭是什么意思(服务器关停)

    服务器关闭是什么意思(服务器关停)

  • 显卡带oc是什么意思(显卡 oc是什么)

    显卡带oc是什么意思(显卡 oc是什么)

  • 钢化膜碎了怎么看屏幕有没有碎(钢化膜碎了怎么撕下来)

    钢化膜碎了怎么看屏幕有没有碎(钢化膜碎了怎么撕下来)

  • 集成运算放大器输入级的特点(集成运算放大器的基本组成单元是)

    集成运算放大器输入级的特点(集成运算放大器的基本组成单元是)

  • 去iphone售后帮验机吗(苹果售后验证手机要钱吗)

    去iphone售后帮验机吗(苹果售后验证手机要钱吗)

  • vivo手机是否支持OTG(vivo手机是否支持nfc功能)

    vivo手机是否支持OTG(vivo手机是否支持nfc功能)

  • 怎么复制自己淘宝账号(怎么复制自己淘宝店铺的短的链接)

    怎么复制自己淘宝账号(怎么复制自己淘宝店铺的短的链接)

  • 京东怎么解除实名认证(京东怎么解除实名认证信息)

    京东怎么解除实名认证(京东怎么解除实名认证信息)

  • iphone11蓝牙搜不到设备怎么办(iphone11蓝牙搜不到新设备)

    iphone11蓝牙搜不到设备怎么办(iphone11蓝牙搜不到新设备)

  • realmex2pro怎么设置翻转静音(realmex2怎么设置图案解锁)

    realmex2pro怎么设置翻转静音(realmex2怎么设置图案解锁)

  • 荣耀20i返回键在哪(荣耀20如何调出返回键)

    荣耀20i返回键在哪(荣耀20如何调出返回键)

  • 天猫精灵怎么联网(天猫精灵怎么联系客服)

    天猫精灵怎么联网(天猫精灵怎么联系客服)

  • 抖音极速版不能拍视频吗(抖音极速版不能赚钱了?)

    抖音极速版不能拍视频吗(抖音极速版不能赚钱了?)

  • 拼多多挂机攒流量怎么关闭(自动刷拼多多挂机软件)

    拼多多挂机攒流量怎么关闭(自动刷拼多多挂机软件)

  • 手机分辨率多少合适(手机分辨率多少最清晰)

    手机分辨率多少合适(手机分辨率多少最清晰)

  • 什么是反避税税率
  • 海关完税价格计算公式
  • 进项税额属于什么账户
  • 白酒消费税税率多少钱一吨
  • 辅导期一般纳税人管理办法
  • 不是公司的车能抵扣进项吗
  • 预付款类发票可抵扣吗
  • 地税补缴社保
  • 没发票的费用怎么做账
  • 小规模季报财报申报错误怎么办
  • 汇票贴现的账务处理
  • 建筑企业增值税怎么算
  • 为在建工程发生的管理费用
  • 外购的半成品属于原材料吗
  • 库存材料盘亏会计分录
  • 税务系统勾了为什么还能认证?
  • 增值税普通发票怎么开
  • 网络技术服务费 税率
  • 信用等级D可以贷款吗
  • 税收编码选错了,发票已经开出去几个月了,还有影响吗
  • 印花税的收取
  • 利润表本月金额指的是什么
  • 高速公路过路费查询网站
  • 期初未缴税额和期末未缴税额
  • 实验设备折旧率
  • 苹果7充电设置在哪里
  • 职工福利费支付范围
  • laravel5 使用try catch的实例详解
  • linux服务器常用命令
  • Win10任务栏没有wifi图标
  • 开办费的账务处理实例
  • 进入苹果icloud
  • 收益类账户的会计分录
  • 无偿调入固定资产账务处理
  • 免抵退税如何进账
  • 客户预付款了尾款怎么办
  • php改编码
  • 应付票据和应收票据的关系
  • 如何理解土地使用制度
  • thinkphp技巧
  • python采集器
  • php页面显示代码
  • php fopen函数的用法
  • 发票报销入账
  • 出售自建不动产怎么交税
  • 应交税金应交增值税科目设置
  • python怎么访问属性
  • python模块的扩展名
  • MySQL主从数据库搭建
  • mysql索引原理及慢查询优化
  • 织梦使用教程
  • 所得税费用是属于费用类吗
  • 利息分月计提按多少
  • 权益法和成本法的适用范围
  • Centos5.5中安装Mysql5.5过程分享
  • 增值税专用发票金额与付款金额是否必须一致
  • 哪些费用不得计税
  • 固定资产的资本化和费用化
  • 暂估入库结转成本
  • 无形资产摊销怎么做记账凭证
  • 普通发票的开具是怎样?
  • 餐饮有普通发票吗
  • 行政事业单位基本户核算内容
  • 年数总和法计提折旧的例题
  • 简单介绍春节的风俗
  • 数据库中经常用的函数
  • freebsd软件安装
  • wp程序
  • centos sudoers
  • 富泽园假日酒店
  • 如何在windows制作表格
  • linux批量处理
  • NGUI字体图集导致Label文字破碎的BUG以及解决方案
  • gitlab管理员账号 审批
  • Cocos2dx3.2 CrazyTetris 物理引擎使用初步
  • js设置图片宽度
  • android应用程序的主要语言是
  • android sdkmanager
  • 个人所得税完税证明
  • 可转债中签当天扣款吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设