hasattr() 与 try-except 处理不存在的属性

99 投票
13 回答
36671 浏览
提问于 2025-04-15 11:48
if hasattr(obj, 'attribute'):
    # do somthing

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

哪个更好,为什么?

13 个回答

34

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

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 的情况,你需要使用一个不同的哨兵值。

95

有没有什么基准测试能说明性能的差异?

使用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
89

hasattr这个函数内部快速地完成了和try/except块一样的工作:它是一个非常具体、优化过的工具,专门用来做一件事情。因此,在合适的情况下,使用它比使用更通用的try/except要更好。

撰写回答