hasattr()和dir()中的'attribute'有什么区别?

9

例如:

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

Python文档中提到,hasattr是通过调用getattr并查看是否引发异常来实现的。然而,这会导致很大的开销,因为获取的值被丢弃,可能会引发异常。

问题是,调用'attribute' in dir(obj)是否意味着相同的事情?它是否更快、更安全,或者在特定场合可能失败?

4个回答

17

这并不完全相同。 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__()的方法,则将调用此方法并必须返回属性列表。

这意味着,如果实现了.__dir__()方法,则hasattr()dir()可能会发现更多的属性。

只需使用hasattr()即可。首先它更快,因为测试属性是便宜的,这只是在一个或多个字典中进行成员资格测试。另一方面,在实例、类和基类之间枚举所有字典键并将它们合并具有更高的CPU成本。


1
使用hasattr的缺点是它会触发属性执行,这可能是昂贵的。在某些情况下,比如Django的OneToOneField关系,这甚至会触发数据库查询。我不认为有其他选择,对吧? - augustomen
@AugustoMen:您可以使用应用程序特定的测试方法来检查属性是否存在。例如,我相信Django有一种枚举字段的方法。 - Martijn Pieters

7

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

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


1

hasattr()基本上是

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

而“attribute in dir(s)”更像是:

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中,请求宽恕比请求许可更容易。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接