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

  • 怎样运营好一个公司的官方微博?(怎样运营好一个店铺)

    怎样运营好一个公司的官方微博?(怎样运营好一个店铺)

  • 腾讯文档权限设置在哪里(腾讯文档权限设置失败)

    腾讯文档权限设置在哪里(腾讯文档权限设置失败)

  • iwatch6心电图怎么开启(iwatch6心电图怎么用)

    iwatch6心电图怎么开启(iwatch6心电图怎么用)

  • 苹果手机闹钟声音在哪里设置(苹果手机闹钟声音和铃声一样大吗)

    苹果手机闹钟声音在哪里设置(苹果手机闹钟声音和铃声一样大吗)

  • 苹果xr面容id设置不了(苹果xr面容id设置失败)

    苹果xr面容id设置不了(苹果xr面容id设置失败)

  • qq怎么把头像变成正方形(QQ怎么把头像变灰)

    qq怎么把头像变成正方形(QQ怎么把头像变灰)

  • 抖音能用手机号搜索吗(抖音能用手机号注册吗)

    抖音能用手机号搜索吗(抖音能用手机号注册吗)

  • 苹果不显示sim卡联系人(苹果不显示sim卡联系人怎么办)

    苹果不显示sim卡联系人(苹果不显示sim卡联系人怎么办)

  • soul广场可见和主页可见区别(soul广场可见和陌生人可见)

    soul广场可见和主页可见区别(soul广场可见和陌生人可见)

  • 笔记本电脑一小时用多少度电(笔记本电脑一小时多少电费)

    笔记本电脑一小时用多少度电(笔记本电脑一小时多少电费)

  • 小米移动卡可以在其他手机用吗(小米移动卡可以选号吗)

    小米移动卡可以在其他手机用吗(小米移动卡可以选号吗)

  • netware和cad是系统软件吗(cad .net framework3.5)

    netware和cad是系统软件吗(cad .net framework3.5)

  • 拉黑和正在通话的区别(拉黑 正在通话)

    拉黑和正在通话的区别(拉黑 正在通话)

  • 手机4g后面带hd什么意思(4g后面的hd是什么)

    手机4g后面带hd什么意思(4g后面的hd是什么)

  • psd手机怎么打开(psd格式手机怎么打开)

    psd手机怎么打开(psd格式手机怎么打开)

  • 天猫小二怎么联系(天猫小二怎么联网的)

    天猫小二怎么联系(天猫小二怎么联网的)

  • ios13怎么关自动亮度(ios13.6怎么关闭自动更新)

    ios13怎么关自动亮度(ios13.6怎么关闭自动更新)

  • 苹果11有超广角吗(iphone12pro有超广角吗)

    苹果11有超广角吗(iphone12pro有超广角吗)

  • 抖音申诉要多久(抖音申诉要多久通过)

    抖音申诉要多久(抖音申诉要多久通过)

  • 小米手机照相模糊原因(小米手机照相模式怎样退出)

    小米手机照相模糊原因(小米手机照相模式怎样退出)

  • word文档表格怎么制作(word文档表格怎么合并单元格)

    word文档表格怎么制作(word文档表格怎么合并单元格)

  • 创建还原点该输入什么(创建还原点后如何还原)

    创建还原点该输入什么(创建还原点后如何还原)

  • vivox27三指截屏怎么设置(vivox23三指截屏)

    vivox27三指截屏怎么设置(vivox23三指截屏)

  • 微信办理etc怎么取消(微信办理etc怎么样)

    微信办理etc怎么取消(微信办理etc怎么样)

  • 华为手机的小程序在哪里(华为手机的小程序怎么设置屏幕下拉)

    华为手机的小程序在哪里(华为手机的小程序怎么设置屏幕下拉)

  • qq如何实名认证(qq如何实名认证游戏)

    qq如何实名认证(qq如何实名认证游戏)

  • 手机删掉的文件怎么恢复(手机删掉的文件在哪里找)

    手机删掉的文件怎么恢复(手机删掉的文件在哪里找)

  • 任务栏图标调大小教程(任务栏图标调大了怎么办)

    任务栏图标调大小教程(任务栏图标调大了怎么办)

  • python可变数据类型和不可变数据类型的区别(Python可变数据类型和不可变数据类型)

    python可变数据类型和不可变数据类型的区别(Python可变数据类型和不可变数据类型)

  • 总公司是小规模分公司是一般纳税人
  • 消费税的计算方法有哪三种
  • 不能抵扣的增值税进项税额是什么
  • 开具红字专用发票的条件
  • 如何查询继续教育证书
  • 开民工工资专户
  • 油票发票在报销中怎么开
  • 增值税应税销售额包括哪些
  • 一般纳税人当月生效次月生效有什么区别
  • 软件行业个人所得税
  • 稽查补缴增值税做账
  • 小规模无票收入怎么做账
  • 境外企业国内签订合同如何缴纳印花税?
  • 公司闲置房租出去违法吗
  • 补发以前年度工资如何计税
  • 未摊销的费用怎样处理
  • 工业企业水电费如何分摊
  • 补贴与工资薪金一起发放,属于福利费,还是纳入工资薪金总额税前扣除?
  • 税务师几年内考完几门
  • 现金短缺属于出纳员负责如何做会计处理
  • 额外收入如何避税
  • 现金存入对公账户用途写什么
  • 报税时利润表没写怎么办
  • 1697509419
  • 小型微利企业普惠性企业所得税减免政策解读
  • 出口货物退免税凭证资料应当保存几年
  • 非主营业务收入记什么
  • 社保基数如何计算单位和个人承担多少
  • 在window操作系统中
  • 在windows7中用来存放硬盘中被删除的文件或文件夹的是
  • 小规模公司注销流程及费用
  • mac触控板使用技巧
  • 按工资额提取福利费
  • 异构图神经网络 电影推荐
  • php fetchall
  • 包装物逾期是什么意思
  • 购买加油卡能否抵扣
  • 已领待用物资
  • 个体工商户如何给员工交社保
  • php扩展开发参考手册
  • 以前年度损益调整结转到哪里
  • 基于深度学习的车型识别系统(Python+清新界面+数据集)
  • 计算md5值命令
  • php与服务器的关系
  • Using Visual Leak Detector
  • 税负率一般控制什么
  • 劳务费800以上扣税标准
  • 税票和发票的区别图片
  • 变卖固定资产的账务处理
  • 租赁期间因占有、使用租赁物获得的收益
  • 劳务分包预缴税款计算公式
  • 以前年度少结转成本怎么办
  • 其他权益工具投资公允价值变动计入什么科目
  • 工程施工的成本
  • 一年以上的借款是长期借款吗
  • 为什么预付账款可以通过应付账款核算
  • 财政登记证取消了吗
  • 贷内部往来
  • 在MySQL中使用GTIDs复制协议和中断协议的教程
  • sqlserver数据库定时任务
  • Linux Container(LXC容器)的基本命令使用简介
  • centos安装选项怎么选
  • win8系统咋样
  • 浮动浮动
  • cocos2dx屏幕适配解决方案
  • 判断输入的日期是一年中第几天
  • meta 标签
  • JQuery和HTML5 Canvas实现弹幕效果
  • 搭建nodejs
  • 结合mint-ui移动端下拉加载实践方法总结
  • jQuery插件库
  • 安卓view事件分发
  • python二叉树
  • JavaScript和HTML DOM的区别与联系及Javascript和DOM的关系
  • 如何在标题栏显示图片
  • 广西地税局局长
  • 定额发票是如何开具的
  • 西安车辆购置税在线交费
  • 税务网上申报怎样操作
  • 差额开票扣除额多补税
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设