6

例如:

>>> s = 'string'
>>> hasattr(s, 'join')
True
>>> 'join' in dir(s)
True

Python 文档说这hasattr是实现调用getattr并查看它是否引发异常。但是,这会导致很大的开销,因为获得的值会被丢弃并且可能会引发异常。

问题是调用是否'attribute' in dir(obj)意味着同样的事情,是更快、更安全,还是在特定场合会失败?

4

4 回答 4

13

这不完全一样。dir()是一种诊断工具,它忽略了getattr()那些会hasattr()找到的属性。

dir()文档中:

默认dir()机制对不同类型的对象表现不同,因为它试图产生最相关的,而不是完整的信息:

  • 如果对象是模块对象,则列表包含模块属性的名称。
  • 如果对象是类型或类对象,则列表包含其属性的名称,并递归地包含其基类的属性。
  • 否则,该列表包含对象的属性名称、其类属性的名称,以及其类的基类属性的递归名称。

注意:因为主要是为了方便在交互式提示中使用dir()而提供的,所以它尝试提供一组有趣的名称,而不是尝试提供一组严格或一致定义的名称,并且其详细行为可能会随版本而变化。例如,当参数是类时,属性不在结果列表中。metaclass

强调我的。

这意味着hasattr()它将找到元类提供的属性,但dir()不会,并且发现的内容可能因 Python 版本而异,因为该函数的定义是为了提供调试便利,而不是完整性。

特定元类场景的演示,其中hasattr()找到元类定义的属性:

>>> class Meta(type):
...     foo = 'bar'
... 
>>> class Foo(metaclass=Meta):
...     pass
... 
>>> hasattr(Foo, 'foo')
True
>>> 'foo' in dir(Foo)
False

最后但并非最不重要的:

如果对象有一个名为 的方法__dir__(),该方法将被调用并且必须返回属性列表。

这意味着,hasattr()如果一个方法已经实现dir(),那么在“找到”哪些属性方面可能会发生更广泛的变化。.__dir__()

坚持下去hasattr()。一方面,它更快,因为测试属性很便宜,因为这只是针对一个或多个字典的成员资格测试。另一方面,枚举所有字典键并将它们跨实例、类和基类合并具有高得多的 CPU 成本。

于 2013-07-18T12:37:44.670 回答
4

hasattr 快 100 倍以上 :)

In [137]: s ='string'

In [138]: %timeit hasattr(s, 'join')
10000000 loops, best of 3: 157 ns per loop

In [139]: %timeit 'join' in dir(s)
100000 loops, best of 3: 19.3 us per loop
于 2013-07-18T12:37:29.307 回答
1

dir()不调用getattr(),或类似的东西,它取决于类来“描述”自己:

>>> class Foo(object):
...     def __dir__(self):
...         return ['apples', 'bananas', 'mangoes']
...     def __getattr__(self, attr):
...         return {'a': 1}[attr]
...     
>>> foo = Foo()
>>> hasattr(foo, 'a')
True
>>> hasattr(foo, 'apples')
False
>>> 'a' in dir(foo)
False
>>> 'apples' in dir(foo)
True

您应该只dir()在查找文档时使用。

于 2013-07-18T12:37:48.653 回答
0

hasattr() 基本上是

try:
    s.attribute
    return True
except AttributeError:
    return False

而“目录中的属性”更像是:

for attr in dir(s):
    if attribute == attr:
        return True
return False

因此,预计 hasattr 会更快一些。

无论如何,如果允许我偏离轨道,那么我会建议这样做。如果你想做类似的事情:

if hasattr(s, 'attributeName'):
    s.attributeName()
else:
    do_stuff()

那么建议这样做:

try:
    s.attributeName()
except AttributeError:
    do_stuff()

为什么?

  1. 为了避免额外的 try-except 块/ for 循环的开销。
  2. 在 python 中,请求宽恕比请求许可更容易。
于 2013-07-18T12:46:03.733 回答