hasattr()vs try-except块来处理不存在的属性
if hasattr(obj, 'attribute'): # do somthing
VS
try: # access obj.attribute except AttributeError, e: # deal with AttributeError
哪个应该是首选的,为什么?
hasattr
内部迅速地执行与try/except
块相同的任务:这是一个非常具体的,优化的,单任务的工具,因此在适用时应该优先select通用的替代scheme。
任何说明性能差异的长椅?
时间是你的朋友
$ 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: ca 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
:这是大多数情况下的正确select。
有问题的情况是,当一个类重写__getattr__
: hasattr
将捕获所有的exception,而不是像您期望的那样捕获AttributeError
。 换句话说,下面的代码将会打印b: False
即使看到一个ValueError
exception会更合适:
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
。
一个简单的解决方法是编写一个这样的实用程序function:
_notset = object() def safehasattr(thing, attr): return getattr(thing, attr, _notset) is not _notset
这让我们来处理一下情况,然后可以提出适当的例外。
我想说这取决于你的函数是否可以接受没有devise属性的对象,例如,如果你有两个调用函数,一个提供一个对象的属性,另一个提供没有它的对象。
如果唯一的情况下,你会得到一个没有属性的对象是由于一些错误,我会build议使用exception机制,即使它可能会更慢,因为我相信这是一个更清洁的devise。
底线:我认为这是一个devise和可读性问题,而不是效率问题。
还有第三种,也是更好的select:
attr = getattr(obj, 'attribute', None) if attr is not None: print attr
优点:
-
getattr
不具有Martin Geiser指出的exception吞咽行为 – 在旧的Pythons中,hasattr
甚至会吞下KeyboardInterrupt
。 -
你检查对象是否有属性的正常原因是你可以使用该属性,这自然会导致它。
-
该属性是自动读取的,并且可以从其他线程更改对象的安全。 (但是,如果这是一个主要问题,您可能需要考虑在访问对象之前locking对象)。
-
它比
try/finally
短,通常比hasattr
短。 -
except AttributeError
其他的AttributeErrors
都可以捕获到所期望的其他AttributeErrors
,这会导致混淆行为。 -
访问一个属性比访问一个局部variables慢(特别是如果它不是一个普通的实例属性)。 (虽然说实话,Python中的微观优化通常是愚蠢的事情。)
需要注意的一件事情是,如果你关心obj.attribute
设置为None的情况,你需要使用一个不同的哨兵值。
如果这只是你正在testing的一个属性,我会说使用hasattr
。 但是,如果您要对可能存在或不存在的属性进行多次访问,则使用try
块可能会为您节省一些键入空间。
从实际的angular度来看,在大多数语言中使用条件总是比处理exception要快得多。
如果你想要处理当前函数之外某个属性不存在的情况,那么exception是最好的方法。 您可能想要使用exception而不是条件的指示符是条件只是设置标志并中止当前操作,而其他地方的其他位置将检查该标志并基于该标志采取措施。
也就是说,正如Rax Olgud所指出的那样,与他人的沟通是代码的一个重要属性,而你所说的“这是一种特殊的情况”而不是“这是我希望发生的事情”可能更重要。
我build议选项2.如果其他线程正在添加或删除属性,选项1有一个竞争条件。
另外python有一个成语 ,EAFP(比容许更容易要求原谅)比LBYL(“看你跳跃”之前)好。
如果不具有该属性不是错误条件,则exception处理变体有一个问题:在访问obj.attribute时,也会捕获可能在内部发生的AttributeErrors(例如,因为属性是一个属性,因此访问它会调用一些代码)。
至less在程序到底发生了什么时,忽略了人的可读性等部分(实际上大部分时间比性能更重要(至less在这种情况下 – 具有这种性能),正如罗伊·阿德勒和其他人所指出的那样)。
尽pipe如此,从这个angular度来看,它然后成为一个select之间的问题
try: getattr(obj, attr) except: ...
和
try: obj.attr except: ...
因为hasattr
只是使用第一种情况来确定结果。 食物的思想;-)
首先。
越短越好。 例外情况应该是例外。