今天同事反馈说我写的一个基础库有一个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老司机相关推荐

  1. Python老司机总结新手常见10大错误

    作为python的新手,大家在上手python的时候都会或多或少的接触到一些rookie mistake,也就是菜鸟级别的错误.小编作为一个老司机也是从这些错误中一点一点的积累经验,才慢慢的能够熟练的 ...

  2. 【机器学习】树模型遇上类别型特征(Python)

    在数据挖掘项目的数据中,数据类型可以分为两种:有序的连续数值 和 无序的类别型特征. 对于xgboost.GBDT等boosting树模型,基学习通常是cart回归树,而cart树的输入通常只支持连续 ...

  3. Python老司机给上路新手的3点忠告

    在你学习编程的过程中,是否有过以下经历,或正在面临类似的局面: ● 网上找了很多资料,不知道从哪里看起 ● 买了本书,过了很久也没有看完第一章 ● 开始还能跟着写几行代码,后来突然就看不懂了 ● 也看 ...

  4. 【无删减】Python老司机收藏夹的17个国外免费学习网站

    用Python编写代码一点都不难,事实上它一直被赞誉为最容易学的编程语言.如果你准备学习web开发, Python是一个不错的开始,甚至想做游戏的话,用Python来开发游戏的资源也有很多.这是快速学 ...

  5. Python老司机带你快速搞定日志分析工具

    标题说明一切,原文地址:jkklee/web_log_analyse,觉得文章不错请给原作者一个star哦~ 日志分析在web系统中故障排查.性能分析方面有着非常重要的作用.该工具的侧重点不是通常的P ...

  6. Python老司机手把手带你写爬虫,整站下载妹子图,一次爽个够!

    其实很多编程语言都可以做爬虫,例如java.c#.php等等甚至excel都可以抓网页的图表,那么为什么我们要用Python呢?它简单.便捷,而且有好多库可以选择,可以说python是写爬虫的首选了! ...

  7. python老司机带你玩玩炫酷的3D渲染,酷毙了!

    自从学会了turtle模块后,画了不少简笔画.像小猪佩奇.哆啦A梦等等,但是这些依然不能让身边人感受到python的强大,依然是每次装逼必被打脸.就像你用turtle画了这个 别人拿出了这个 怎么办? ...

  8. 都在推介 TS,但 TS 真的有必要上吗?来看看老司机怎么说

    点击上方蓝字关注前端下午茶,从此前端进阶不再难 文章转载自港台作者:Ting-Shu Lin,文末有原文链接. 大家好,我是京都开发室的Lin. 在工作与私人专案中使用TypeScript开发已约两年 ...

  9. springboot设置默认值_线上Bug无法复现?老司机教你一招,SpringBoot远程调试不用愁!...

    前言 在部署线上项目时,相信大家都会遇到一个问题,线上的 Bug 但是在本地不会复现,多么无奈. 此时最常用的就是取到前端传递的数据用接口测试工具测试,比如 POSTMAN,复杂不,难受不? 今天陈某 ...

最新文章

  1. java jdk下载过慢 解决方案
  2. 【C++】Visual Studio教程(二) - 代码编辑器
  3. solr 启动时指定 solr.home
  4. 【SDOI2018】战略游戏【圆方树】【虚树】
  5. 图片合成gif_谈谈有哪些好用的制作GIF的方式
  6. python tcp服务器模板_python socket之tcp服务器与客户端示例
  7. 爬虫3 requests基础之下载图片用content(二进制内容)
  8. Smartmail外贸CRMBuild1.0版系统白皮书
  9. 360服务器已停止响应,对于 SQL Server 2017年累积更新 5
  10. Ubuntu 携手初创企业用代码开拓物联网
  11. PCL中把txt文件转换成.pcd文件(很简单)
  12. 编程语言对高手没有差别,对低手差别太明显
  13. 软件可行性研究报告模板
  14. 是时候觉悟了!一篇文章让你明白数据结构与算法分析有多重要
  15. bert tensorflow2 serving部署
  16. AD15批量修改引脚名字的方法
  17. PS2021中文汉化版软件安装教程
  18. 《程序员升职记》第九年 零保护行动
  19. 实用epub阅读器分享
  20. 变形金刚ol服务器维护,变形金刚OL5月27日停机更新延迟开服公告

热门文章

  1. redis篇-基础与应用篇(上)
  2. go mod 设置代理下载依赖包,溜溜的
  3. 明日方舟 -19年我最喜欢的手游
  4. batch size 代码
  5. iNFTnews | NFT带来音乐行业的革命,音乐家和粉丝的互动将更亲密
  6. 线性代数基础13--相似矩阵与奇异值分解
  7. Nexus 6P 解锁+TWRP+CM
  8. gjc02转wgs84 基于postgis
  9. numpy中resize和reshape的区别
  10. 文件open、流fopen、fdopen