hasattr()vs try-except块来处理不存在的属性

时间:2009-05-24 05:12:00

标签: python exception exception-handling attributes hasattr

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

VS

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

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

12 个答案:

答案 0 :(得分:79)

任何能够说明性能差异的长椅?

timeit这是你的朋友

$ 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

答案 1 :(得分:77)

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

答案 2 :(得分:18)

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

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

优点:

  1. getattr没有错误exception-swallowing behavior pointed out by Martin Geiser - 在旧蟒蛇中,hasattr甚至会吞下KeyboardInterrupt

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

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

  4. 它比try/finally短,通常比hasattr短。

  5. 广泛的except AttributeError区块可以捕获其他AttributeErrors区块而不是您期望的区域,这可能会导致行为混乱。

  6. 访问属性比访问本地变量要慢(特别是如果它不是普通的实例属性)。 (虽然,老实说,Python中的微优化通常是一个愚蠢的错误。)

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

答案 3 :(得分:17)

我几乎总是使用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')

重要的错误因此消失了。这是fixed in Python 3.2issue9666),其中hasattr现在只捕获AttributeError

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

_notset = object()

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

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

答案 4 :(得分:13)

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

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

结论:我认为这是设计和可读性问题,而不是效率问题。

答案 5 :(得分:4)

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

答案 6 :(得分:4)

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

答案 7 :(得分:3)

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

此外,python有一个Idiom,EAFP(“更容易请求宽恕而不是许可”)优于LBYL(“在你跳跃之前看”)。

答案 8 :(得分:2)

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

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

如上所述,正如Rax Olgud指出的那样,与他人沟通是代码的一个重要属性,你想说的是“这是一种特殊的情况”,而不是“这是我希望发生的事情”更重要的是。

答案 9 :(得分:2)

Sebastian Witowski在EuroPython 2016演讲 Writing faster Python 中介绍了这个主题。这是他的幻灯片的再现与性能总结。在本次讨论中,他还使用术语看一下,这里值得一提的是标记该关键字。

  

如果该属性实际上缺失,那么请求原谅   比要求权限慢。所以作为一个经验法则,你可以   如果知道很有可能,请使用ask for permission方式   属性将丢失或您可以预测的其他问题。   否则,如果您希望代码在大多数情况下都能读取   代码

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

答案 10 :(得分:0)

第一个。

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

答案 11 :(得分:0)

至少当它取决于程序中发生的事情时,忽略了人类可读性等等(实际上大部分时间比性能更重要(至少在这种情况下)具有该性能如罗伊阿德勒和其他人指出的那样)。

然而从这个角度来看, 然后它变成了在

之间进行选择的问题
try: getattr(obj, attr)
except: ...

try: obj.attr
except: ...

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