How are Python's Built In Dictionaries Implemented 上已经有一个有趣的任务...
但为什么不在这里尝试测量(在阅读了有关 python 实现中的时间复杂性之后):
#! /usr/bin/env python
from __future__ import division, print_function
import dis # for disassembling (bottom)
import string # string.printable as sample character source
import timeit
chars = tuple(string.printable)
cartesian = tuple(a + b for a in chars for b in chars)
assert 10000 == len(cartesian), print(len(cartesian))
d = dict((a + b, b) for a in cartesian for b in chars)
assert 1000000 == len(d), print(len(d))
assert d['zzz'] == 'z'
setup = """
import string
chars = tuple(string.printable)
d = dict((a + b, b) for a in chars for b in chars)
"""
assert 1000000 / 10000 == 100
setup_100x = """
import string
chars = tuple(string.printable)
cartesian = tuple(a + b for a in chars for b in chars)
d = dict((a + b, b) for a in cartesian for b in chars)
"""
stmt = """
'zzz' in d
"""
t = timeit.timeit(stmt=stmt, setup=setup, timer=timeit.default_timer,
number=timeit.default_number)
print("# Timing[secs] for 1x10000:", t)
t_100x = timeit.timeit(stmt=stmt, setup=setup_100x, timer=timeit.default_timer,
number=timeit.default_number)
print("# Timing[secs] for 100x10000:", t_100x)
disassemble_me = "'zzz' in {'a': 'b'}"
print("# Disassembly(lookup in dict with 1 string entry):")
print("#", disassemble_me)
dis.dis(disassemble_me)
disassemble_me = "'zzz' in {'a': 'b', 'c': 'd'}"
print("# Disassembly(lookup in dict with 2 string entries):")
print("#", disassemble_me)
dis.dis(disassemble_me)
disassemble_me = "'zzz' in {'a': 'b', 'c': 'd', 'e': 'f'}"
print("# Disassembly(lookup in dict with 3 string entries):")
print("#", disassemble_me)
dis.dis(disassemble_me)
在我使用 Python 2.7.11 的机器上,这给出了:
# Timing[secs] for 1x10000: 0.0406861305237
# Timing[secs] for 100x10000: 0.0472030639648
# Disassembly(lookup in dict with 1 string entry):
# 'zzz' in {'a': 'b'}
0 <39>
1 SETUP_FINALLY 31354 (to 31358)
4 <39>
5 SLICE+2
6 BUILD_MAP 8302
9 <123> 24871
12 <39>
13 INPLACE_DIVIDE
14 SLICE+2
15 <39>
16 DELETE_GLOBAL 32039 (32039)
# Disassembly(lookup in dict with 2 string entries):
# 'zzz' in {'a': 'b', 'c': 'd'}
0 <39>
1 SETUP_FINALLY 31354 (to 31358)
4 <39>
5 SLICE+2
6 BUILD_MAP 8302
9 <123> 24871
12 <39>
13 INPLACE_DIVIDE
14 SLICE+2
15 <39>
16 DELETE_GLOBAL 11303 (11303)
19 SLICE+2
20 <39>
21 DUP_TOPX 14887
24 SLICE+2
25 <39>
26 LOAD_CONST 32039 (32039)
# Disassembly(lookup in dict with 3 string entries):
# 'zzz' in {'a': 'b', 'c': 'd', 'e': 'f'}
0 <39>
1 SETUP_FINALLY 31354 (to 31358)
4 <39>
5 SLICE+2
6 BUILD_MAP 8302
9 <123> 24871
12 <39>
13 INPLACE_DIVIDE
14 SLICE+2
15 <39>
16 DELETE_GLOBAL 11303 (11303)
19 SLICE+2
20 <39>
21 DUP_TOPX 14887
24 SLICE+2
25 <39>
26 LOAD_CONST 11303 (11303)
29 SLICE+2
30 <39>
31 LOAD_NAME 14887 (14887)
34 SLICE+2
35 <39>
36 BUILD_TUPLE 32039
所以 10000 个条目在大约 10^4 个条目字典中查找“zz”。平均 40 毫秒(timeit.default_number == 1000000),低于 50 毫秒,100 倍,即 10^6 个条目('zzz'lookup)。
# Timing[secs] for 1x10000: 0.0406861305237
# Timing[secs] for 100x10000: 0.0472030639648
测量意味着可重复性 :-) 因此再次运行它:
# Timing[secs] for 1x10000: 0.0441079139709
# Timing[secs] for 100x10000: 0.0460820198059
它只是解决了(这里没有显示其他运行围绕具有这些键类型和长度关系的字典(较大字典的键也更长!)这里存在 no 线性最坏情况实现了。对于 100 倍大的 dict(qua 条目计数)和 50% 大的密钥长度,运行时间更像是 10%。
看起来不错。建议在有疑问时始终测量和拆卸。
HTH。
PS:OP 可能仍希望在未来的问题中提供更多代码上下文,因为最好在知道如何使用数据结构的情况下选择它;-)
PPS:Raymond Hettinger 等人。经常用“热爱细节”来优化 CPython 实现(对不起,我没有更好的英文表达),所以总是期望针对小“尺寸”问题的特定展开实现,这就是玩具变体问题的反汇编可能有很大差异的原因从一开始,就实现了大型任务。这就是为什么我更喜欢 timeit 和(配置文件测量)而不是反汇编,但我们应该习惯于读取字节码,以便在测量的性能无法满足我们的期望时获得想法。
否则:享受查找的反汇编:-)
更新:...如果您更改声明时间,即小 dict 收到命中 'zz' 而大 dict 没有(反之亦然),您也可能遇到这些时间:
# Timing[secs] for 1x10000: 0.0533709526062
# Timing[secs] for 100x10000: 0.0458760261536
'zz' in d 的测试需要 53 毫秒(小)和 46 毫秒(大)(平均 1000000 次试验)。