鉴于:
>>> d = {'a': 1, 'b': 2}
以下哪项是检查 'a'
是否在 d
中的最佳方法?
>>> 'a' in d
True
>>> d.has_key('a')
True
in
胜出,不仅在优雅方面(并且没有被弃用;-),而且在性能方面,例如:
$ python -mtimeit -s'd=dict.fromkeys(range(99))' '12 in d'
10000000 loops, best of 3: 0.0983 usec per loop
$ python -mtimeit -s'd=dict.fromkeys(range(99))' 'd.has_key(12)'
1000000 loops, best of 3: 0.21 usec per loop
虽然以下观察结果并非总是正确,但您会注意到通常,在 Python 中,更快的解决方案更优雅且更符合 Python 风格;这就是 -mtimeit
如此有用的原因 - 只是在这里和那里节省一百纳秒!-)
has_key
似乎也是 O(1)。
当(且仅当)您的代码需要可由早于 2.3 的 Python 版本(引入 key in dict
时)运行时,使用 dict.has_key()
。
有一个示例 in
实际上会影响您的性能。
如果您在仅实现 __getitem__
和 has_key()
但未实现 __contains__
的 O(1) 容器上使用 in
,您会将 O(1) 搜索变为 O(N) 搜索(因为 in
落入通过 __getitem__
回到线性搜索)。
修复显然是微不足道的:
def __contains__(self, x):
return self.has_key(x)
has_key()
特定于 Python 2 字典。 in
/ __contains__
是要使用的正确 API;对于那些无法避免全面扫描的容器,没有 has_key()
方法无论如何,如果有 O(1) 方法,那么这将是特定于用例的,因此取决于开发人员为问题选择正确的数据类型。
不推荐使用 dict.has_key() 的解决方案,使用 'in' -- sublime text editor 3
在这里,我举了一个名为'ages'的字典示例 -
ages = {}
# Add a couple of names to the dictionary
ages['Sue'] = 23
ages['Peter'] = 19
ages['Andrew'] = 78
ages['Karren'] = 45
# use of 'in' in if condition instead of function_name.has_key(key-name).
if 'Sue' in ages:
print "Sue is in the dictionary. She is", ages['Sue'], "years old"
else:
print "Sue is not in the dictionary"
用 Adam Parkin 的评论扩展 Alex Martelli 的性能测试……
$ python3.5 -mtimeit -s'd=dict.fromkeys(range( 99))' 'd.has_key(12)'
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 301, in main
x = t.timeit(number)
File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 178, in timeit
timing = self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
d.has_key(12)
AttributeError: 'dict' object has no attribute 'has_key'
$ python2.7 -mtimeit -s'd=dict.fromkeys(range( 99))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0872 usec per loop
$ python2.7 -mtimeit -s'd=dict.fromkeys(range(1999))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0858 usec per loop
$ python3.5 -mtimeit -s'd=dict.fromkeys(range( 99))' '12 in d'
10000000 loops, best of 3: 0.031 usec per loop
$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d'
10000000 loops, best of 3: 0.033 usec per loop
$ python3.5 -mtimeit -s'd=dict.fromkeys(range( 99))' '12 in d.keys()'
10000000 loops, best of 3: 0.115 usec per loop
$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d.keys()'
10000000 loops, best of 3: 0.117 usec per loop
has_key
是一种字典方法,但 in
将适用于任何集合,即使缺少 __contains__
,in
也会使用任何其他方法迭代集合以找出答案。
range
对象进行 in
测试实际上非常有效。不过,我不太确定它在 Python 2 xrange
上的效率。 ;)
__contains__
可以简单地计算 值是否在范围内。
range
实例的开销。使用单个 预先存在的 实例,“范围内的整数”测试在我的计时中快了大约 40%。
如果你有这样的事情:
if d.has_key('a'):
将其更改为以下以在 Python 3.X 及更高版本上运行:
if 'a' in d:
ew
引用也是字典中的键,则 t.has_key(ew)
返回 True
。如果字典中的值是 not,则 key not in t
返回 True
。此外,key = ew
别名非常非常多余。正确的拼写是 if ew in t
。这是 8 年前公认的答案已经告诉你的。
不定期副业成功案例分享
keys()
只是字典中的类似集合的视图,而不是副本,因此x in d.keys()
是 O(1)。不过,x in d
更符合 Python 风格。x in d.keys()
必须构造和销毁一个临时对象,完成所需的内存分配,其中x in d.keys()
只是进行算术运算(计算哈希)并进行查找。请注意,d.keys()
仅是此长度的 10 倍左右,实际上还不算长。我还没有检查,但我仍然很确定它只是 O(1)。