19

我试图找出确定是否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'

哪种方法是首选方法(为什么)?

4

3 回答 3

12

我相信最后一种方法通常是 Python 编码人员首选的方法,因为 Python 社区有一句座右铭:“请求宽恕比请求许可更容易”(EAFP)。

简而言之,座右铭的意思是避免在做某事之前检查您是否可以做某事。相反,只需运行该操作。如果失败,请适当处理。

此外,第三种方法还有一个额外的优点,就是清楚地表明该操作应该有效。


话虽如此,您确实应该避免使用这样的裸机except。这样做将捕获任何/所有异常,甚至是不相关的异常。相反,最好专门捕获异常。

在这里,您将要捕获AttributeError

try:
    obj.do_stuff()   # Try to invoke do_stuff
except AttributeError:
    print 'Do something else'  # If unsuccessful, do something else
于 2014-01-09T15:57:16.123 回答
7

检查与使用鸭子类型isinstance的 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 - 它更具可读性。

于 2014-01-09T16:00:07.037 回答
4

正确的答案是“都不”hasattr 提供功能,但它可能是所有选项中最糟糕的。

我们使用 python 的面向对象特性,因为它可以工作。OO 分析永远不会准确并且经常令人困惑,但是我们使用类层次结构,因为我们知道它们可以帮助人们更快地更好地工作。人们掌握对象,一个好的对象模型可以帮助编码人员更快地改变事物并且减少错误。正确的代码最终聚集在正确的位置。对象:

  • 可以直接使用而不考虑存在哪个实现
  • 明确需要更改的内容和位置
  • 将某些功能的更改与某些其他功能的更改隔离开来——您可以修复 X ​​而不必担心会破坏 Y

hasattr 与 isinstance

必须使用 isinstance 或 hasattr 表明对象模型已损坏或我们使用不正确。正确的做法是修复对象模型或改变我们使用它的方式。这两个构造具有相同的效果,并且在命令式“我需要代码来执行此操作”意义上它们是等效的。结构上有很大的不同。在第一次遇到这种方法时(或在做了几个月的其他事情之后),isinstance 传达了更多关于实际发生的事情和其他可能的信息。Hasattr 不会“告诉”你任何事情。

悠久的开发历史使我们远离了 FORTRAN 和带有大量“我是谁”开关的代码。我们选择使用对象是因为我们知道它们有助于使代码更易于使用。通过选择 hasattr,我们提供了功能,但没有任何东西是固定的,代码比我们开始之前更糟糕。将来添加或更改此功能时,我们将不得不处理分组不均且至少具有两个组织原则的代码,其中一些在“应该”的地方,其余的则随机分散在其他地方。没有什么可以使它连贯一致。这不是一个错误,而是散布在通过你的 hasattr 的任何执行路径上的潜在错误的雷区。

因此,如果有任何选择,顺序是:

  1. 使用对象模型或修复它,或者至少弄清楚它有什么问题以及如何修复它
  2. 使用 isinstance
  3. 不要使用 hasattr
于 2016-04-07T14:00:02.063 回答