我试图找出确定对象obj
是否可以执行操作do_stuff()
的不同方法之间的权衡。据我了解,有三种方法可以确定这是否可行:
# Way 1
if isinstance(obj, Foo):
obj.do_stuff()
# Way 2
if hasattr(obj, 'do_stuff'):
obj.do_stuff()
# Way 3
try:
obj.do_stuff()
except:
print 'Do something else'
哪种方法首选(以及为什么)?
答案 0 :(得分:9)
我认为Python编码器通常首选最后一种方法,因为Python社区教授了motto:“更容易请求宽恕而非许可”(EAFP)。
简而言之,座右铭意味着避免在你做之前检查你是否可以做某事。相反,只需运行该操作。如果失败,请妥善处理。
此外,第三种方法还有一个额外的好处,即明确操作应该工作。
话虽如此,你真的应该避免像这样使用裸except
。这样做会捕获任何/所有异常,甚至是不相关的异常。相反,最好特别捕获异常。
在这里,您需要捕获AttributeError
:
try:
obj.do_stuff() # Try to invoke do_stuff
except AttributeError:
print 'Do something else' # If unsuccessful, do something else
答案 1 :(得分:6)
使用isinstance
进行检查会违反使用duck typing的Python惯例。
hasattr
工作正常,但Look Before you Leap代替更多Pythonic EAFP。
您对第3种方式的实施很危险,因为它可以捕获任何和所有错误,包括do_stuff
方法引发的错误。你可以更精确地使用:
try:
_ds = obj.do_stuff
except AttributeError:
print('Do something else')
else:
_ds()
但在这种情况下,我更喜欢方式2,尽管有轻微的开销 - 它更具可读性。
答案 2 :(得分:3)
正确的答案是'不是' hasattr提供功能,但它可能是所有选项中最差的。
我们使用python的面向对象特性,因为它有效。面向对象分析从来都不准确,而且经常会混淆我们使用类层次结构,因为我们知道它们可以帮助人们更快地完成工作。人们掌握物体,良好的物体模型可以帮助编码人员更快地改变事物,减少错误。正确的代码最终聚集在正确的位置。对象:
hasattr vs isinstance
必须使用isinstance或hasattr表示对象模型已损坏或我们使用不正确。正确的做法是修复对象模型或更改我们使用它的方式。 这两个结构具有相同的效果,并且在命令式“我需要代码执行此操作”的意义上它们是等效的。在结构上存在巨大差异。在第一次实现这种方法时(或者在做了其他事情的几个月之后),isinstance会传达更多有关实际情况和其他可能性的信息。 Hasattr没有告诉你任何事情。
长期的开发历史使我们远离FORTRAN和代码与大量的“我是谁”交换机。我们选择使用对象,因为我们知道它们有助于使代码更易于使用。通过选择hasattr我们提供功能但是没有任何修复,代码比我们开始之前更加破碎。在将来添加或更改此功能时,我们将不得不处理不同组合的代码,并且至少有两个组织原则,其中一些是“应该”的,其余的则随机分散在其他地方。没有什么可以使它凝聚在一起。这不是一个错误,而是潜在错误的雷区分散在通过你的地址的任何执行路径上。
因此,如果有任何选择,订单是: