我最近一直在阅读一些tweets和python documentation关于hasattr的内容,它说:
hasattr(对象,名称)
参数是一个对象和一个字符串。如果字符串是>>的名称,则结果为True。对象的一个属性,否则为False。 (这是通过调用来实现的 getattr(object,name)并查看它是否引发了AttributeError。)
Python中有一句座右铭是要求宽恕而不是许可我通常同意。
在这种情况下,我尝试使用一个非常简单的python代码进行性能测试:
import timeit
definition="""\
class A(object):
a = 1
a = A()
"""
stm="""\
hasattr(a, 'a')
"""
print timeit.timeit(stmt=stm, setup=definition, number=10000000)
stm="""\
getattr(a, 'a')
"""
print timeit.timeit(stmt=stm, setup=definition, number=10000000)
结果:
$ python test.py
hasattr(a, 'a')
1.26515984535
getattr(a, 'a')
1.32518696785
如果属性不存在且getattr和hasattr之间的差异更大,我也尝试了会发生什么。所以我到目前为止看到的是getattr比hasattr慢,但在文档中它说它调用了getattr。
我搜索了hasattr和getattr的CPython实现,似乎都调用了下一个函数:
v = PyObject_GetAttr(v, name);
但是getattr中的样板比hasattr中的样板更多,这可能使它更慢。
有没有人知道为什么在文档中我们说hasattr调用getattr并且我们似乎鼓励用户使用getattr而不是hasattr,当它真的不是由于性能?只是因为它更加pythonic?
也许我在测试中做错了什么:)
谢谢,
劳尔
答案 0 :(得分:16)
文档不鼓励,文档只是陈述了显而易见的。 hasattr
是这样实现的,并且从属性getter中抛出AttributeError
可以使它看起来像属性不存在。这是一个重要的细节,这就是文档中明确说明的原因。例如,考虑以下代码:
class Spam(object):
sausages = False
@property
def eggs(self):
if self.sausages:
return 42
raise AttributeError("No eggs without sausages")
@property
def invalid(self):
return self.foobar
spam = Spam()
print(hasattr(Spam, 'eggs'))
print(hasattr(spam, 'eggs'))
spam.sausages = True
print(hasattr(spam, 'eggs'))
print(hasattr(spam, 'invalid'))
结果是
True
False
True
False
即Spam
类有eggs
的属性描述符,但由于getter在AttributeError
时引发not self.sausages
,因此该类的实例不会#&# 34; hasattr
" eggs
。
除此之外,仅当不需要值时才使用hasattr
;如果您需要该值,请使用带有2个参数的getattr
并捕获异常或3个参数,第三个是合理的默认值。
使用getattr()
(2.7.9)的结果:
>>> spam = Spam()
>>> print(getattr(Spam, 'eggs'))
<property object at 0x01E2A570>
>>> print(getattr(spam, 'eggs'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in eggs
AttributeError: No eggs without sausages
>>> spam.sausages = True
>>> print(getattr(spam, 'eggs'))
42
>>> print(getattr(spam, 'invalid'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in invalid
AttributeError: 'Spam' object has no attribute 'invalid'
>>>
答案 1 :(得分:2)
似乎hasattr
吞咽异常存在问题(至少in Python 2.7),所以最好远离它,直到它被修复。
>>> class Foo(object):
... @property
... def my_attr(self):
... raise ValueError('nope, nope, nope')
...
>>> bar = Foo()
>>> bar.my_attr
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_attr
ValueError: nope, nope, nope
>>> hasattr(Foo, 'my_attr')
True
>>> hasattr(bar, 'my_attr')
False
>>> getattr(bar, 'my_attr', None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_attr
ValueError: nope, nope, nope
>>>