if hasattr(obj, 'attribute'):
# do somthing
对比
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
哪个应该是首选,为什么?
if hasattr(obj, 'attribute'):
# do somthing
对比
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
哪个应该是首选,为什么?
有没有说明性能差异的长凳?
时间它是你的朋友
$ 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
hasattr
在内部快速执行与块相同的任务try/except
:它是一个非常具体、优化的单任务工具,因此在适用时,应优先于非常通用的替代方案。
还有第三种,通常是更好的选择:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
好处:
getattr
没有Martin Geiser 指出的糟糕的异常吞咽行为——在旧 Python 中,hasattr
甚至会吞下KeyboardInterrupt
.
您检查对象是否具有属性的正常原因是您可以使用该属性,这自然会导致它。
该属性以原子方式读取,并且不受其他线程更改对象的影响。(不过,如果这是一个主要问题,您可能需要考虑在访问对象之前锁定它。)
它比 更短,try/finally
而且通常比 更短hasattr
。
一个宽泛的except AttributeError
块可能会捕获AttributeErrors
您所期望的之外的块,这可能会导致令人困惑的行为。
访问一个属性比访问一个局部变量要慢(特别是如果它不是一个普通的实例属性)。(不过,老实说,Python 中的微优化通常是徒劳的。)
需要注意的一件事是,如果您关心obj.attribute
设置为 None 的情况,则需要使用不同的哨兵值。
我几乎总是使用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
处理这种情况,然后它可以引发适当的异常。
我想说这取决于您的函数是否可以接受没有设计属性的对象,例如,如果您有两个函数调用者,一个提供具有属性的对象,另一个提供没有属性的对象。
如果你得到一个没有属性的对象的唯一情况是由于一些错误,我建议使用异常机制,即使它可能会更慢,因为我相信它是一个更简洁的设计。
底线:我认为这是一个设计和可读性问题,而不是效率问题。
如果没有属性不是错误条件,则异常处理变体有一个问题:它还会捕获访问 obj.attribute 时可能在内部出现的 AttributeErrors(例如,因为属性是一个属性,因此访问它会调用一些代码)。
这个主题在Sebastian Witowski的 EuroPython 2016 演讲编写更快的 Python中有所涉及。这是他的幻灯片和性能摘要的复制品。他还使用了您在此讨论中跳跃之前的术语外观,值得一提的是在这里标记该关键字。
如果该属性实际上缺失,那么请求宽恕将比请求权限慢。因此,根据经验,如果知道该属性很可能会丢失或您可以预测的其他问题,您可以使用请求许可方式。否则,如果您期望代码将导致大多数时间可读代码
# 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
如果它只是您正在测试的一个属性,我会说 use hasattr
。但是,如果您正在对可能存在或不存在的属性进行多次try
访问,那么使用块可能会为您节省一些输入。
我建议选项 2。如果其他线程正在添加或删除该属性,则选项 1 具有竞争条件。
python也有一个成语,即EAFP('比许可更容易请求宽恕')比LBYL('在你跳跃之前看看')更好。
从实际的角度来看,在大多数语言中,使用条件总是比处理异常快得多。
如果您想处理当前函数之外某处不存在属性的情况,则例外是更好的方法。您可能希望使用异常而不是条件的一个指标是,条件仅设置一个标志并中止当前操作,其他地方检查该标志并根据该标志采取行动。
也就是说,正如 Rax Olgud 所指出的,与他人交流是代码的一个重要属性,你想说“这是一种特殊情况”而不是“这是我期望发生的事情”可能更重要.
首先。
越短越好。例外应该是例外。
至少当它取决于程序中发生的事情时,忽略了可读性等人为部分(实际上大多数时候这比性能更重要(至少在这种情况下 - 具有那个性能跨度),正如 Roee Adler 和其他人指出的那样)。
尽管如此,从这个角度来看,它就变成了在两者之间进行选择的问题
try: getattr(obj, attr)
except: ...
和
try: obj.attr
except: ...
因为hasattr
只使用第一种情况来确定结果。值得深思 ;-)