当__getattr__()遇上@property,坑倒Python老司机
今天同事反馈说我写的一个基础库有一个bug,大概就是自己写的类明明有属性foo
,但会抛个类似下边的异常出来,
AttributeError: 'A' object has no attribute 'foo'
这很让人困惑啊,因为抛出异常的函数是基类的__getattr__()
方法,所以他就找我来解决了。
我看代码也是一脸懵,这个foo
就摆在那里,这个bug给了我一个眼见不为实的错觉,一时找不到方向。突然我发现这个foo
上面顶着个@property
的帽子(装饰器),咦,会不会和这个有关系呢?于是搜索一下,就找到了这篇文章Correct handling of AttributeError in __getattr__ when using property,上面的高赞回答完美解答了这个问题。下面我就以这个问答的代码为例再讲一下,这样大家可以只看我这篇了。
首先有这样的代码,这个代码纯作示例用的,没有任何逻辑意义,
class A:@propertydef F(self):return self.moo # here should be an error@propertydef G(self):return self.Fdef __getattr__(self, name):print('call of __getattr__ with name =', name)if name == 'foo':return 0raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, name))a = A()
print(a.G)
显然地,会以为抛出的异常是AttributeError: 'A' object has no attribute 'moo'
,但实际抛出的异常却是AttributeError: 'A' object has no attribute 'G'
而且吐出来的堆栈是这样的:
Traceback (most recent call last):line 18 in <module>print(a.G)line 15, in __getattr__raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, name))
AttributeError: 'A' object has no attribute 'G'
作为__getattr__()
的作者,简直能气死个人。
消消气,那么为什么__getattr__()
会把前面AtrributeError
异常吃掉了呢?这就要先回顾一下__getatrr__()
的调用时机,文档中说:
Called when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or __get__() of a name property raises AttributeError).
简单来说,当查找属性时,Python
会先调用__getattribute__()
,如果找不到(抛出AtrributeError
异常),才会去尝试调用__getattr__()
。还有一种情况就是如果property
的__get__()
方法抛出AttributeError
异常时,也会尝试调用__getattr__()
。
那么回到上例,使用@property
修饰的成员函数就成一个描述器descriptor
,当访问它的时候,实际调用的是描述器的__get__()
方法,显然,print(a.G)
先调用了a.G.__get__
,然后它又调用了a.F.__get__
,而这个方法引用了self.moo
,因为moo
属性不存在,就抛出了AtrributeError
。根据上面文档的描述,此时会调用a
的__getattr__()
方法,根据上面的代码,这个__getattr__()
也会抛出AtrributeError
,所以Python就把前一个异常吃掉了,只显示了后面那个牛头不对马嘴的异常。
如此说来,那就是要在__get__()
抛出AtrributeError
的时候接住异常,而不是由Python去处理。要实现这一点,在这个例子和我们项目的代码里,都是重载__getatrribute__()
而不是__getattr__()
。把上面的代码修改如下:
class A:@propertydef F(self):return self.moo # here should be an error@propertydef G(self):return self.Fdef __getattribute__(self, name):print('call of __getattribute__ with name =', name)if name == 'foo':return 0else:return super().__getattribute__(name)
再次执行的时候,就会发现抛出来的异常符合期望了:
Traceback (most recent call last):
...
AttributeError: 'A' object has no attribute 'moo'
而我们项目做了同样的修改之后,也解决了问题。这个事故告诉我们,轻易不要去重载__getattr__()
。
当__getattr__()遇上@property,坑倒Python老司机相关推荐
- Python老司机总结新手常见10大错误
作为python的新手,大家在上手python的时候都会或多或少的接触到一些rookie mistake,也就是菜鸟级别的错误.小编作为一个老司机也是从这些错误中一点一点的积累经验,才慢慢的能够熟练的 ...
- 【机器学习】树模型遇上类别型特征(Python)
在数据挖掘项目的数据中,数据类型可以分为两种:有序的连续数值 和 无序的类别型特征. 对于xgboost.GBDT等boosting树模型,基学习通常是cart回归树,而cart树的输入通常只支持连续 ...
- Python老司机给上路新手的3点忠告
在你学习编程的过程中,是否有过以下经历,或正在面临类似的局面: ● 网上找了很多资料,不知道从哪里看起 ● 买了本书,过了很久也没有看完第一章 ● 开始还能跟着写几行代码,后来突然就看不懂了 ● 也看 ...
- 【无删减】Python老司机收藏夹的17个国外免费学习网站
用Python编写代码一点都不难,事实上它一直被赞誉为最容易学的编程语言.如果你准备学习web开发, Python是一个不错的开始,甚至想做游戏的话,用Python来开发游戏的资源也有很多.这是快速学 ...
- Python老司机带你快速搞定日志分析工具
标题说明一切,原文地址:jkklee/web_log_analyse,觉得文章不错请给原作者一个star哦~ 日志分析在web系统中故障排查.性能分析方面有着非常重要的作用.该工具的侧重点不是通常的P ...
- Python老司机手把手带你写爬虫,整站下载妹子图,一次爽个够!
其实很多编程语言都可以做爬虫,例如java.c#.php等等甚至excel都可以抓网页的图表,那么为什么我们要用Python呢?它简单.便捷,而且有好多库可以选择,可以说python是写爬虫的首选了! ...
- python老司机带你玩玩炫酷的3D渲染,酷毙了!
自从学会了turtle模块后,画了不少简笔画.像小猪佩奇.哆啦A梦等等,但是这些依然不能让身边人感受到python的强大,依然是每次装逼必被打脸.就像你用turtle画了这个 别人拿出了这个 怎么办? ...
- 都在推介 TS,但 TS 真的有必要上吗?来看看老司机怎么说
点击上方蓝字关注前端下午茶,从此前端进阶不再难 文章转载自港台作者:Ting-Shu Lin,文末有原文链接. 大家好,我是京都开发室的Lin. 在工作与私人专案中使用TypeScript开发已约两年 ...
- springboot设置默认值_线上Bug无法复现?老司机教你一招,SpringBoot远程调试不用愁!...
前言 在部署线上项目时,相信大家都会遇到一个问题,线上的 Bug 但是在本地不会复现,多么无奈. 此时最常用的就是取到前端传递的数据用接口测试工具测试,比如 POSTMAN,复杂不,难受不? 今天陈某 ...
最新文章
- java jdk下载过慢 解决方案
- 【C++】Visual Studio教程(二) - 代码编辑器
- solr 启动时指定 solr.home
- 【SDOI2018】战略游戏【圆方树】【虚树】
- 图片合成gif_谈谈有哪些好用的制作GIF的方式
- python tcp服务器模板_python socket之tcp服务器与客户端示例
- 爬虫3 requests基础之下载图片用content(二进制内容)
- Smartmail外贸CRMBuild1.0版系统白皮书
- 360服务器已停止响应,对于 SQL Server 2017年累积更新 5
- Ubuntu 携手初创企业用代码开拓物联网
- PCL中把txt文件转换成.pcd文件(很简单)
- 编程语言对高手没有差别,对低手差别太明显
- 软件可行性研究报告模板
- 是时候觉悟了!一篇文章让你明白数据结构与算法分析有多重要
- bert tensorflow2 serving部署
- AD15批量修改引脚名字的方法
- PS2021中文汉化版软件安装教程
- 《程序员升职记》第九年 零保护行动
- 实用epub阅读器分享
- 变形金刚ol服务器维护,变形金刚OL5月27日停机更新延迟开服公告