Python宽恕与权限和鸭子输入


44

在Python中,我经常听到“乞求宽恕”(捕获异常)比“询问许可”(类型/条件检查)更好。关于在Python中强制鸭子输入,这是

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

胜过或坏于

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

在性能,可读性,“ pythonic”或其他重要因素方面?


17
还有第三种选择,什么也不做,将没有bar的任何foo视为bug
jk。

我记得在内部听到的hasattr是通过确切的try / catch实现的。不确定它是否正确...(它在属性上的作用不同,不是吗?也许我在想getattr..)
Izkata 2012年

@Izkata:的实现hasattr确实使用了C-API等效项getattrTrue如果成功False则返回,否则返回),但是在C中处理异常要快得多。
Martijn Pieters 2012年

2
我已经接受了martijn的回答,但是我想补充一点,如果您要设置属性,则绝对应该考虑使用try / catch,因为它可能是没有设置器的属性,在这种情况下hasattr将为true ,但仍会引发AttributeError。
darkfeline 2012年

Answers:


60

这实际上取决于您认为将要抛出异常的频率。

在我看来,这两种方法至少在可读性和Python风格方面都同样有效。但是,如果90%的对象都没有该属性,则bar您会注意到两种方法之间存在明显的性能差异:

>>> import timeit
>>> def askforgiveness(foo=object()):
...     try:
...         x = foo.bar
...     except AttributeError:
...         pass
... 
>>> def askpermission(foo=object()):
...     if hasattr(foo, 'bar'):
...         x = foo.bar
... 
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789

但是,如果90%的对象确实具有属性,则表已被翻转:

>>> class Foo(object):
...     bar = None
... 
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541

因此,从性能的角度来看,您需要选择最适合您的情况的方法。

最后,对timeit模块的某些战略性使用可能是您可以做的最Python化的事情。


1
几周前,我问了一个问题:programmers.stackexchange.com/questions/161798/… 在那里,我问是否在松散类型的语言中是否必须额外进行类型检查,而有人说我没有这样做,这使我感到震惊。知道我看到你有。
图兰斯·科尔多瓦

@ user1598390:定义一个期望类型混合的API时,必须进行一些测试。大多数情况下,您不需要。恐怕这是您无法从特定区域获得关于范式整体的规则的领域。
马丁·彼得

好吧,任何认真的系统开发都涉及定义API。因此,我猜想类型严格的语言是最好的选择,因为编译器会在编译时为您检查类型,因此您不必编写太多代码。
图兰斯·科尔多瓦

1
@GarethRees:它为答案的后半部分建立了一种模式,在该模式中,我将一个参数传递给被测函数。
Martijn Pieters

1
请注意,对于hasattr,它实际上实际上在幕后做了一个try-except的C-api等效项,因为事实证明,确定对象在Python中是否具有属性的唯一通用方法是尝试访问它。
user2357112

11

在python中,您通常可以以Python的方式来获得更好的性能。对于其他语言,将异常用于流控制通常被认为是一个糟糕的主意,因为异常通常会带来非常大的开销。但是,由于此技术已在Python中明确认可,因此解释器已针对此类代码进行了优化。

与所有性能问题一样,确定的唯一方法是分析代码。编写两个版本,看看哪个版本运行更快。尽管以我的经验来看,“ Python方式”通常是最快的方式。


3

我认为性能是次要的问题。如果出现这种情况,探查器将帮助您专注于真正的瓶颈,这可能是也可能不是您如何对待可能的非法论点的方法。

另一方面,可读性和简单性始终是首要关注的问题。这里没有硬性规定,只需使用您的判断即可。

这是一个普遍的问题,但是特定于环境或语言的约定是相关的。例如,在Python中,通常可以简单地使用您期望的属性,并让可能的AttributeError到达调用者。


-1

正确性而言,我认为异常处理是解决问题的方法(不过,有时我自己也使用hasattr()方法)。依赖于hasattr()的基本问题是,它将违反代码约定的行为转换为无提示的失败(这是JavaScript中的一个大问题,不会引发不存在的属性)。


3
除了别人已经说过的话,您的答案似乎没有多大作用。与其他网站不同,程序员希望获得答案,这些答案将解释答案背后的原因。您用无声的故障问题触及了一个良点,但没有为此提供正义。阅读下面可能会有帮助:programmers.stackexchange.com/help/how-to-answer

我最后的回答被批评为范围太广。我以为我会尽量简短。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.