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

  • 用AR增强现实技术做广告和营销的正确姿势(ar增强现实技术的方式)

    用AR增强现实技术做广告和营销的正确姿势(ar增强现实技术的方式)

  • 怎样利用QQ群推广自己的网店?(怎么在qq群里推荐好友)

    怎样利用QQ群推广自己的网店?(怎么在qq群里推荐好友)

  • 电压低电脑自动重启(电压低电脑自动关机)(电压低电脑自动关机怎么办)

    电压低电脑自动重启(电压低电脑自动关机)(电压低电脑自动关机怎么办)

  • access中用来表示实体的是(在access中,使用1表示是)

    access中用来表示实体的是(在access中,使用1表示是)

  • 抖音超级乐迷是怎么来的(抖音超级乐迷是什么)

    抖音超级乐迷是怎么来的(抖音超级乐迷是什么)

  • 京东单单减最高99是什么意思(京东单单减最高299一般减多少)

    京东单单减最高99是什么意思(京东单单减最高299一般减多少)

  • 红米k30移动信号经常无服务(红米k305g版信号差)

    红米k30移动信号经常无服务(红米k305g版信号差)

  • 佳能打印机墨盒灯一直闪烁(佳能打印机墨盒怎么加墨水)

    佳能打印机墨盒灯一直闪烁(佳能打印机墨盒怎么加墨水)

  • 网易云加入黑名单别人会知道吗(网易云加入黑名单发消息显示什么)

    网易云加入黑名单别人会知道吗(网易云加入黑名单发消息显示什么)

  • 抖音极速版和抖音短视频有什么区别(抖音极速版和抖音账号互通吗)

    抖音极速版和抖音短视频有什么区别(抖音极速版和抖音账号互通吗)

  • cctalk是什么(cctalk软件)

    cctalk是什么(cctalk软件)

  • 华为nova5支持什么快充(华为nova5支持什么卡)

    华为nova5支持什么快充(华为nova5支持什么卡)

  • 火山小视频和抖音是一家吗(火山小视频和抖音那个先出来)

    火山小视频和抖音是一家吗(火山小视频和抖音那个先出来)

  • 文字波浪线怎么设置(文字波浪线怎么去掉)

    文字波浪线怎么设置(文字波浪线怎么去掉)

  • 手机上本地音乐怎样清除(手机本地音乐在哪)

    手机上本地音乐怎样清除(手机本地音乐在哪)

  • 荣耀9x支持面部解锁吗(荣耀9x面部识别在哪)

    荣耀9x支持面部解锁吗(荣耀9x面部识别在哪)

  • 魅族16sPro耗电太快怎么办(魅族16s耗电快)

    魅族16sPro耗电太快怎么办(魅族16s耗电快)

  • oppoflndx支持多少w快充(oppofindx支持5g吗)

    oppoflndx支持多少w快充(oppofindx支持5g吗)

  • ivox27微信怎么设置美颜(手机微信如何设置)

    ivox27微信怎么设置美颜(手机微信如何设置)

  • 企查查怎么导出数据(企查查怎么导出一万多)

    企查查怎么导出数据(企查查怎么导出一万多)

  • 呼到死怎么屏蔽

    呼到死怎么屏蔽

  • 腾讯王卡免流是无限吗(腾讯王卡免流是不是就是定向流量)

    腾讯王卡免流是无限吗(腾讯王卡免流是不是就是定向流量)

  • 触宝电话如何去除广告(触宝电话怎样)

    触宝电话如何去除广告(触宝电话怎样)

  • Windows11家庭版本地账户不能登入微软账户怎么办(windows11家庭版本地策略组)

    Windows11家庭版本地账户不能登入微软账户怎么办(windows11家庭版本地策略组)

  • 出口退税工作流程及内容
  • 股权转让开票怎么做账
  • 事业单位不用纳税吗
  • 企业对企业分红要缴纳什么税免税分红
  • 管理费用贷方有哪些递减
  • 小规模开票软件怎么下载
  • 新会计准则 预付房租怎么做账
  • 收到房屋租金税率
  • 出售投资性房地产时,按其账面价值结转到主营业务成本
  • 客运地方税务局监制发票还能用吗?
  • 国际货运代理0税率和免税
  • 收到投资款怎么做记账凭证
  • 润滑油消费税征税环节
  • 预付房租收到发票后如何做账
  • 年中股东红利分录怎么写
  • 企业给员工缴纳的五险最低交多少
  • 股东分红如何合理节税
  • 银行开户许可证什么时候开始取消的
  • 福利企业的增值税是多少
  • 企业公司报税流程
  • 劳务派遣公司支付劳务费怎么做账
  • 企业所得税法的主要内容
  • 附加税包括哪些税种2021年
  • u盘如何进行杀毒
  • 鸿蒙侧边菜单
  • 开增值税发票折扣额怎么开出来的?
  • php使用composer
  • 分期付款购买的商品
  • window7无法正常启动怎么办
  • nodejs安装教程
  • 非营利组织免税资格公示
  • 库存现金日记账的登记依据
  • 北极野生动物
  • 进料加工贸易方式
  • 公允价值变动损益在利润表哪里
  • 班迪录屏软件录制框怎样在ppt上一直指定
  • php隐藏图片地址
  • 增值税折扣发票有折扣字样
  • 新企业所得税季初资产总额怎么填
  • 网上学电脑的软件
  • 总公司签协议,分公司开票,报账怎样写说明
  • 异地工程税
  • 高铁票的电子发票
  • 非金融企业借款利息收入计入什么科目
  • 公司月报和季报
  • 小企业会计准则和一般企业会计准则的区别
  • 成本费用率和成本费用利润率的区别
  • 收到政府扶贫款如何做分录
  • mysql复制数据到另一张表
  • 税控盘维护费280元怎么做账
  • 小规模结转成本怎么计算
  • 全年一次性奖金并入或单独哪个好
  • 资产减值准备的计提影响递延所得税资产吗
  • 所得税汇算清缴退税会计分录怎么做
  • 增值税及附加是什么税
  • 扣税时账上没钱怎么办
  • 工会会费收入计入会计科目
  • 影院会计有前景吗
  • 采购入库单如何弃审U8
  • 贷款公司如何确定利率
  • 无发票的费用怎么算
  • sql server 错误
  • mysql数据库迁移上云
  • windows vista 版本
  • windows2003文件服务器
  • shutdown /f
  • 默认终端地址
  • vnetd.exe
  • win 10文件
  • pcalc是什么软件
  • linux系统怎么弄
  • Windows 7 Apache下计算机无法访问局域网网站的解决方法
  • android aidl binder
  • 在Android EditText中实现日期时间选择器(DatePicker和TimePicker)
  • python中的字符型
  • 木瓜电子
  • 低丰度油气田开采的原油
  • 西安二手房几年可以过户
  • 预测收入遵循什么方法
  • 图书行业税率多少
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设