93
if hasattr(obj, 'attribute'):
    # do somthing

对比

try:
    # access obj.attribute
except AttributeError, e:
    # deal with AttributeError

哪个应该是首选,为什么?

4

12 回答 12

93

有没有说明性能差异的长凳?

时间它是你的朋友

$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "nonexistent")'
1000000 loops, best of 3: 1.87 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "a")'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
 c.a
except:
 pass'
1000000 loops, best of 3: 0.247 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
 c.nonexistent
except:
 pass'
100000 loops, best of 3: 3.13 usec per loop
$

       |positive|negative
hasattr|  0.446 |  1.87 
try    |  0.247 |  3.13
于 2009-05-24T06:41:27.237 回答
88

hasattr在内部快速执行与块相同的任务try/except:它是一个非常具体、优化的单任务工具,因此在适用时,应优先于非常通用的替代方案。

于 2009-05-24T05:17:15.250 回答
30

还有第三种,通常是更好的选择:

attr = getattr(obj, 'attribute', None)
if attr is not None:
     print attr

好处:

  1. getattr没有Martin Geiser 指出的糟糕的异常吞咽行为——在旧 Python 中,hasattr甚至会吞下KeyboardInterrupt.

  2. 您检查对象是否具有属性的正常原因是您可以使用该属性,这自然会导致它。

  3. 该属性以原子方式读取,并且不受其他线程更改对象的影响。(不过,如果这是一个主要问题,您可能需要考虑在访问对象之前锁定它。)

  4. 它比 更短,try/finally而且通常比 更短hasattr

  5. 一个宽泛的except AttributeError块可能会捕获AttributeErrors您所期望的之外的块,这可能会导致令人困惑的行为。

  6. 访问一个属性比访问一个局部变量要慢(特别是如果它不是一个普通的实例属性)。(不过,老实说,Python 中的微优化通常是徒劳的。)

需要注意的一件事是,如果您关心obj.attribute设置为 None 的情况,则需要使用不同的哨兵值。

于 2013-05-24T03:18:35.717 回答
19

我几乎总是使用hasattr: 这是大多数情况下的正确选择。

有问题的情况是当一个类覆盖__getattr__:hasattr捕获所有异常而不是AttributeError像您期望的那样捕获。换句话说,b: False即使看到ValueError异常更合适,也会打印下面的代码:

class X(object):
    def __getattr__(self, attr):
        if attr == 'a':
            return 123
        if attr == 'b':
            raise ValueError('important error from your database')
        raise AttributeError

x = X()
print 'a:', hasattr(x, 'a')
print 'b:', hasattr(x, 'b')
print 'c:', hasattr(x, 'c')

重要的错误就这样消失了。这已在 Python 3.2 ( issue9666 ) 中得到修复,hasattr现在只能捕获AttributeError

一个简单的解决方法是编写这样的实用程序函数:

_notset = object()

def safehasattr(thing, attr):
    return getattr(thing, attr, _notset) is not _notset

这让我们getattr处理这种情况,然后它可以引发适当的异常。

于 2013-04-24T07:32:57.523 回答
13

我想说这取决于您的函数是否可以接受没有设计属性的对象,例如,如果您有两个函数调用者,一个提供具有属性的对象,另一个提供没有属性的对象。

如果你得到一个没有属性的对象的唯一情况是由于一些错误,我建议使用异常机制,即使它可能会更慢,因为我相信它是一个更简洁的设计。

底线:我认为这是一个设计和可读性问题,而不是效率问题。

于 2009-05-24T05:37:42.993 回答
5

如果没有属性不是错误条件,则异常处理变体有一个问题:它还会捕获访问 obj.attribute 时可能在内部出现的 AttributeErrors(例如,因为属性是一个属性,因此访问它会调用一些代码)。

于 2009-06-30T10:52:28.280 回答
5

这个主题在Sebastian Witowski的 EuroPython 2016 演讲编写更快的 Python中有所涉及。这是他的幻灯片和性能摘要的复制品。他还使用了您在此讨论中跳跃之前的术语外观,值得一提的是在这里标记该关键字。

如果该属性实际上缺失,那么请求宽恕将比请求权限慢。因此,根据经验,如果知道该属性很可能会丢失或您可以预测的其他问题,您可以使用请求许可方式。否则,如果您期望代码将导致大多数时间可读代码

3 许可还是宽恕?

# CASE 1 -- Attribute Exists
class Foo(object):
    hello = 'world'
foo = Foo()

if hasatter(foo, 'hello'):
    foo.hello
## 149ns ##

try:
    foo.hello
except AttributeError:
    pass
## 43.1 ns ##
## 3.5 times faster


# CASE 2 -- Attribute Absent
class Bar(object):
    pass
bar = Bar()

if hasattr(bar, 'hello'):
    bar.hello
## 428 ns ##

try:
    bar.hello
except AttributeError :
    pass
## 536 ns ##
## 25% slower
于 2018-03-16T19:12:00.807 回答
4

如果它只是您正在测试的一个属性,我会说 use hasattr。但是,如果您正在对可能存在或不存在的属性进行多次try访问,那么使用块可能会为您节省一些输入。

于 2009-05-24T05:32:01.377 回答
3

我建议选项 2。如果其他线程正在添加或删除该属性,则选项 1 具有竞争条件。

python也有一个成语,即EAFP('比许可更容易请求宽恕')比LBYL('在你跳跃之前看看')更好。

于 2009-05-24T08:00:38.027 回答
2

从实际的角度来看,在大多数语言中,使用条件总是比处理异常快得多。

如果您想处理当前函数之外某处不存在属性的情况,则例外是更好的方法。您可能希望使用异常而不是条件的一个指标是,条件仅设置一个标志并中止当前操作,其他地方检查该标志并根据该标志采取行动。

也就是说,正如 Rax Olgud 所指出的,与他人交流是代码的一个重要属性,你想说“这是一种特殊情况”而不是“这是我期望发生的事情”可能更重要.

于 2009-05-24T05:44:44.980 回答
0

首先。

越短越好。例外应该是例外。

于 2009-05-24T05:14:59.073 回答
0

至少当它取决于程序中发生的事情时,忽略了可读性等人为部分(实际上大多数时候这比性能更重要(至少在这种情况下 - 具有那个性能跨度),正如 Roee Adler 和其他人指出的那样)。

尽管如此,从这个角度来看,它就变成了在两者之间进行选择的问题

try: getattr(obj, attr)
except: ...

try: obj.attr
except: ...

因为hasattr只使用第一种情况来确定结果。值得深思 ;-)

于 2012-10-12T08:29:57.803 回答