例如:
>>> s = 'string'
>>> hasattr(s, 'join')
True
>>> 'join' in dir(s)
True
Python文档中提到,hasattr
是通过调用getattr
并查看是否引发异常来实现的。然而,这会导致很大的开销,因为获取的值被丢弃,可能会引发异常。
问题是,调用'attribute' in dir(obj)
是否意味着相同的事情?它是否更快、更安全,或者在特定场合可能失败?
这并不完全相同。 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成本。
使用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
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()
。
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
if hasattr(s, 'attributeName'):
s.attributeName()
else:
do_stuff()
try:
s.attributeName()
except AttributeError:
do_stuff()
为什么?
hasattr
的缺点是它会触发属性执行,这可能是昂贵的。在某些情况下,比如Django的OneToOneField
关系,这甚至会触发数据库查询。我不认为有其他选择,对吧? - augustomen