if hasattr(obj, 'attribute'):
# do somthing
与
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
应该首选哪个,为什么?
Answers:
hasattr
在内部快速执行与该try/except
块相同的任务:它是一种非常具体,经过优化的单任务工具,因此,在适用时,应优先选择通用用途的工具。
hasattr
将捕获Python 2.x中的所有异常。请参阅我的答案以获取示例和简单的解决方法。
有没有可以说明性能差异的基准?
时间是你的朋友
$ 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
try
则速度约为的两倍hasattr()
。如果不存在,try
则速度要慢大约1.5倍hasattr()
(并且两者都比属性确实存在时要慢得多)。这可能是因为,在幸福的道路上,try
几乎什么都不做(Python已经在支付异常开销,而不管是否使用它们),但是hasattr()
需要名称查找和函数调用。在不幸的道路上,他们俩都必须做一些异常处理和a goto
,但是hasattr()
要用C而不是Python字节码来完成。
还有第三种,通常更好的选择:
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
处理这种情况,然后可以引发适当的异常。
safehasattr
,getattr
如果要使用它,则只需将值复制到一个局部变量中,几乎总是这样。
hasattr
做有所改善。
hasattr
,然后去检查时,我还是不知道。我们有一些有趣的bzr错误,其中hasattr吞噬了^ C。
我要说的是,这取决于您的函数是否可以通过设计接受不带属性的对象,例如,如果您有两个调用该函数的调用者,一个提供一个带有属性的对象,另一个提供一个不带属性的对象。
如果唯一的情况是由于某种错误而导致没有属性的对象,那么我建议您使用异常机制,尽管它可能会更慢,因为我认为这是一种更干净的设计。
底线:我认为这是设计和可读性问题,而不是效率问题。
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
我建议选项2。如果其他一些线程正在添加或删除属性,则选项1具有竞争条件。
python还有一个成语,那就是EAFP(“更容易要求宽恕而不是允许”)比LBYL(“跳前先看”)要好。
从实际的角度来看,在大多数语言中,使用条件语句总是比处理异常更快。
如果要处理当前函数外部某处不存在的属性的情况,则最好使用异常处理。您可能希望使用异常而不是条件的一种指示是,条件仅设置了一个标志并中止了当前操作,其他地方检查了该标志并根据该标志采取了行动。
就是说,正如Rax Olgud所指出的那样,与其他人的交流是代码的重要属性,而您想说“这是一种特殊情况”而不是“这是我期望发生的事情”,这可能更重要。 。
首先。
越短越好。例外应该是例外。
for
语句的末尾都有一个,并且也hasattr
使用了一个。但是,“越短越好”(“越简单越好”!)确实适用,因此更简单,更短,更具体的hasattr确实是可取的。