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

Imr*_*ran 80 python attributes exception-handling exception hasattr

if hasattr(obj, 'attribute'):
    # do somthing
Run Code Online (Sandbox Code Playgroud)

VS

try:
    # access obj.attribute
except AttributeError, e:
    # deal with AttributeError
Run Code Online (Sandbox Code Playgroud)

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

小智 84

任何说明性能差异的长凳?

这是你的朋友

$ 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
Run Code Online (Sandbox Code Playgroud)

  • +1用于提供有趣,有形的数字.事实上,"try"在包含常见情况时是有效的(即当Python异常非常特殊时). (15认同)
  • 我不知道如何解释这些结果。这里哪个更快,快多少? (2认同)
  • @ StevenM.Vascellaro:如果该属性存在,则“ try”的速度大约是“ hasattr()”的两倍。如果没有,“ try”的速度要比“ hasattr()”的速度慢约1.5倍(而且两者都比该属性确实存在的速度要慢得多)。这可能是因为,在一条快乐的道路上,“尝试”几乎无能为力(Python已经在支付异常开销,而不管您是否使用它们),但是“ hasattr()”需要名称查找和函数调用。在不愉快的道路上,他们俩都必须做一些异常处理和一个“ goto”操作,但是“ hasattr()”是用C语言而不是Python字节码完成的。 (2认同)

Ale*_*lli 79

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

  • 除了你仍然需要try/catch块来处理竞争条件(如果你使用线程). (8认同)
  • 一个有趣的[评论](http://stackoverflow.com/a/21025020/1959808):`try`可以传达操作*应该*工作.虽然`try`的意图并不总是这样,但它很常见,所以它可能被认为更具可读性. (4认同)
  • 请注意,`hasattr`将**捕获Python 2.x中的所有异常**.有关示例和简单的解决方法,请参阅[我的回答](http://stackoverflow.com/a/16186050/110204). (3认同)

poo*_*lie 20

还有第三种,通常更好的替代方案:

attr = getattr(obj, 'attribute', None)
if attr is not None:
     print attr
Run Code Online (Sandbox Code Playgroud)

好处:

  1. getattr没有马丁盖泽指出的不良异常 - 吞咽行为 - 在古老的蟒蛇中,hasattr甚至会吞下一个KeyboardInterrupt.

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

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

  4. 它比短于try/finally和短于hasattr.

  5. 除了您期望的那个之外,一个广泛的except AttributeError区块可以捕获AttributeErrors,这可能导致混乱的行为.

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

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


Mar*_*ler 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')
Run Code Online (Sandbox Code Playgroud)

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

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

_notset = object()

def safehasattr(thing, attr):
    return getattr(thing, attr, _notset) is not _notset
Run Code Online (Sandbox Code Playgroud)

这让我们getattr处理这种情况,然后它可以提出适当的例外.

  • 这在Python2.6中也有所改进(http://hg.python.org/cpython/rev/4c8375574aa9/),因此`hasattr`至少不会捕获`KeyboardInterrupt`等. (2认同)

Roe*_*ler 13

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

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

一句话:我认为这是设计和可读性问题,而不是效率问题.

  • +1 坚持为什么“尝试”对于阅读代码的人有意义。:) (2认同)

Unc*_*eiv 5

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


jxr*_*mos 5

这个题目是在EuroPython 2016报告涉及写作更快的Python塞巴斯蒂安Witowski。这是他的幻灯片的复制品和绩效总结。他还在讨论中使用了术语look before you jumping,这里值得一提的是标记该关键字。

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

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
Run Code Online (Sandbox Code Playgroud)