【问题标题】:Python hasattr vs getattrPython hasattr 与 getattr
【发布时间】:2014-09-18 05:06:59
【问题描述】:

我最近一直在阅读一些关于 hasattr 的 tweetspython documentation,上面写着:

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。

我搜索了 hasattrgetattr 的 CPython 实现,似乎都调用了下一个函数:

v = PyObject_GetAttr(v, name);

但 getattr 中的样板文件比 hasattr 中的多,这可能会使其变慢。

有谁知道为什么在文档中我们说 hasattr 调用 getattr 并且我们似乎鼓励用户使用 getattr 而不是 hasattr,但实际上并不是因为性能?仅仅是因为它更pythonic吗?

也许我在测试中做错了什么:)

谢谢,

劳尔

【问题讨论】:

  • 这里的区别不是性能。你说的是5纳秒。仅使用 Python,您就会损失数千倍。这里的区别在于哪一个对你需要做的事情是正确的。
  • 性能差异不是重点。我不明白为什么文档中的注释说明它是通过调用 getattr 实现的。
  • 也许在某一时刻它确实调用了getattr,但现在它没有,但没有人更新文档......?不过,坦率地说,这种声明无论如何都不属于 API 文档。文档应该解释发生了什么,而不是如何发生,除非“如何”是“什么”的重要组成部分。

标签: python python-2.7 python-3.x cpython


【解决方案1】:

文档不鼓励,文档只是说明显而易见的。 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,那么该类的实例不会“hasattreggs

除此之外,仅在您不需要该值时使用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'
>>>

【讨论】:

  • 换句话说,文档是在说善意的谎言
【解决方案2】:

似乎hasattr 有吞咽异常的问题(至少in Python 2.7),所以在它修复之前最好远离它。

the following code为例:

>>> 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
>>>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 2021-07-09
    • 2013-04-09
    • 2013-04-29
    • 2012-05-11
    • 1970-01-01
    相关资源
    最近更新 更多