Python里一个很危险的函数
本文由 Hacker News 上曾经排名第一的文章编译而来,作者 Hynek Schlawack 是一名德国软件工程师。
他建议,除非是编写只兼容 Python 3 的代码而且清楚地了解hasattr()
的用法,否则不要使用hasattr()
。
在 Python 2 下,不建议编写这样的代码:
if hasattr(x, "y"): print(x.y) else: print("no y!")
更好的做法是像如下两例所示:
try: print(x.y) except AttributeError: print("no y!")
或者
y = getattr(x, "y", None) if y is not None: print(y) else: print("no y!")
如果处理的不是用户自行创建的类,更应该采用上述写法。
上面有一处用到了getattr()
,这里将属性缺失视作属性的值为None
(常见的情况),如果想与此区分开来,可使用一个标记值(sentinel value)。getattr
的速度不比hasattr()
低,因为二者的查找过程完全相同,并且后者不会保存结果(至少在CPython实现下是如此)。
为什么不建议使用hasattr()
在 Python 2 下使用hasattr()
,和下面的代码几乎没有分别:
try: print(x.y) except: print("no y!")
但是这样会隐藏掉特性(property):
>>> class C(object): ... @property ... def y(self): ... 0/0 ... >>> hasattr(C(), "y") False
对于第三方库中类,我们无法确定某个属性(attribute)是否为特性(或者之后某次更新将其变成特性),因此上面那样使用hasattr()
是非常危险的。
你或许不信,但是 Hacker News 上确实有程序员回复说经历过这个情况,hasattr()
隐藏了一个非常深的错误,让程序调式工作变得异常艰难。
另外一个原因是,对特性使用hasttr()
会执行它们的 getter 函数,但这样和hasattr()
这个函数的名称并不相符。
不过,在 Python 3 中,hasattr()
不存在这些问题:
>>> class C: ... @property ... def y(self): ... 0/0 ... >>> hasattr(C(), "y") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in y ZeroDivisionError: division by zero
因此,在编写兼容 Python 2 和 3 的混合代码时,要特别注意这个函数。另外,你应该想不到hasattr()
会引发ZeroDivisionError
吧?
留心的读者可能会问,如果出现AttributeError呢??的确,如果真出现,我们没有办法区分到底是因为真的缺失该属性,还是特性存在问题。文首提到的写法可以将可能的错误减少为只有一种,避免出现 Python 2 和 3 之间让人困惑的行为差异。.
结语
当然,在你自己写的代码中仍然可以使用hasattr()
,但是如果后来修改了类,记得也要修改对应的hasattr()
,确保不会出错。不过虽然这样可以少写些代码,但是却增加了不必要的心理负担。
本文文字及图片出自 codingpy.com
你也许感兴趣的:
- 【外评】Python 为何如此糟糕…
- 【外评】用 Python 解释 Rust 背后的思想或理念
- Python 版本之间的主要变化摘要
- 【外评】Python 与苹果应用商店的拒绝作斗争
- 【外评】使用不安全的 Python 将速度提高 100 倍
- 谷歌裁掉整个 Python 团队!PyTorch 创始人急得直骂人:“WTF!核心语言团队无可替换”
- 谷歌Python团队全员被裁——负责内部Python所有基础设施、曾对数亿行代码执行自动重构
- 【译文】Python–一种深受喜爱但永远存在缺陷的语言
- 再同意不过了
- 【译文】减轻 Python 打包的痛苦
你对本文的反应是: