python中的词典理解过于全面

python中的词典理解过于全面,python,performance,dictionary,dictionary-comprehension,Python,Performance,Dictionary,Dictionary Comprehension,使用python2.7,我发现了一个奇怪的时间执行案例: data = dict( zip( a[0].split( ':' ), a[1].split( ':' ) ) ) data = { name: value for name, value in zip(a[0].split( ':' ), a[1].split( ':' )) } 这两个调用对我来说似乎完全相同,但是,我发现字典理解版本大约快4%——不是太多,但非常稳定 这是真的吗?如果是,为什么?还是仅仅是我的想象?您的输入样本

使用python2.7,我发现了一个奇怪的时间执行案例:

data = dict( zip( a[0].split( ':' ), a[1].split( ':' ) ) )

data = { name: value for name, value in zip(a[0].split( ':' ), a[1].split( ':' )) }
这两个调用对我来说似乎完全相同,但是,我发现字典理解版本大约快4%——不是太多,但非常稳定


这是真的吗?如果是,为什么?还是仅仅是我的想象?

您的输入样本太小了。查找全局名称
dict()
比运行dict comprehension(后者不需要名称查找)花费更多的时间,但是如果您针对大量键值对进行测试,则
dict()
将获胜,因为循环完全在C中完成

针对大量的键值对测试差异,并将测试减少到只执行
dict()
调用或字典理解(对于这两种情况,
zip()
str.split()
调用只执行一次,可以忽略):

因此,对于10k键值对(前两次计时测试),
dict()
的速度是前者的两倍,而对于3对键值对(后两次计时测试),dict的理解速度是后者的两倍

当您反编译字节码时,您可以看到原因;字典理解使用嵌套的代码对象来实现实际的字典构建:

>>> import dis
>>> dis.dis(compile('{k: v for k, v in kv_pairs}', '', 'exec'))
  1           0 LOAD_CONST               0 (<code object <dictcomp> at 0x102ef69b0, file "", line 1>)
              3 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (kv_pairs)
              9 GET_ITER            
             10 CALL_FUNCTION            1
             13 POP_TOP             
             14 LOAD_CONST               1 (None)
             17 RETURN_VALUE        
>>> dis.dis(compile('{k: v for k, v in kv_pairs}', '', 'exec').co_consts[0])
  1           0 BUILD_MAP                0
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                21 (to 30)
              9 UNPACK_SEQUENCE          2
             12 STORE_FAST               1 (k)
             15 STORE_FAST               2 (v)
             18 LOAD_FAST                2 (v)
             21 LOAD_FAST                1 (k)
             24 MAP_ADD                  2
             27 JUMP_ABSOLUTE            6
        >>   30 RETURN_VALUE        
>>> dis.dis(compile('dict(kv_pairs)', '', 'exec'))
  1           0 LOAD_NAME                0 (dict)
              3 LOAD_NAME                1 (kv_pairs)
              6 CALL_FUNCTION            1
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        
导入dis >>>dis.dis(compile('{k:v代表k,v在kv_pairs}',''exec')) 1 0加载常数0(<0x102ef69b0处的代码对象dictcomp,文件“”,第1行>) 3生成函数0 6负载名称0(千伏对) 9得到它 10调用函数1 13件流行上衣 14负载常数1(无) 17返回值 >>>dis.dis(compile({k:v代表k,v在kv_pairs},''exec').co_consts[0]) 1 0生成映射0 3加载速度为0(.0) >>国际热核聚变实验堆21(至30)6 9拆包顺序2 12商店快速1(k) 15商店快速2(v) 18负载快速2(v) 21加载速度1(k) 24地图加2 27绝对值6 >>30返回值 >>>dis.dis(编译('dict(kv_对)','exec')) 1 0加载名称0(dict) 3负载名称1(千伏对) 6调用函数1 9件流行上衣 10负载常数0(无) 13返回值
通过使用一个非常小的样本,您为
dict
的权重过大给出了
LOAD\u NAME
步骤;dict理解需要更多字节码,每次迭代都要执行。

那么这里有多少项?查找全局名称
dict()
也需要时间,如果
zip()。如果您确实想比较,请坚持使用固定的已知输入,而不是使用
zip()
str.split()
。生成器只生成一次值,但
timeit
测试运行1000次。生成器第一次生成键值对时,其余的测试将生成空字典;以后,请提出新问题,而不是用新问题更新现有帖子。@MartijnPieters,是的,关于问题,你是对的,对不起。
co_consts[0]
包含什么?代码对象,但它来自哪里?它是否与上面的
MAKE_函数
相对应?@JeromeJ:dict理解(以及生成器表达式和集合理解,在python3中,列表理解)有自己的范围。通过将字节码作为一个函数执行来实现的引擎盖下;第一个反编译显示
LOAD_CONST
加载索引0处的常量,并从中创建一个函数对象(
MAKE_function
),然后使用
kv_pairs
调用该对象,作为传入的iterable。在
compile
调用创建的codeobject上,您可以在
co_consts
元组中找到编译器创建的所有常量。您完全正确,我的输入非常小,每个a[i]中大约有几个项。我试图将它增加到10-15个元素,而dict已经毫无疑问地获胜了。实际上,我不会想象dict的理解与代码中的dict()调用有太大的不同。你知道吗,在python3中dict()的理解速度比dict还要快吗?@Dmitriborisevich:是的,在python3中,对于已经生成的键值对,dict()的理解速度还是比dict快。当您需要为生成的每个键和/或值使用表达式时,请使用dict理解。
>>> import dis
>>> dis.dis(compile('{k: v for k, v in kv_pairs}', '', 'exec'))
  1           0 LOAD_CONST               0 (<code object <dictcomp> at 0x102ef69b0, file "", line 1>)
              3 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (kv_pairs)
              9 GET_ITER            
             10 CALL_FUNCTION            1
             13 POP_TOP             
             14 LOAD_CONST               1 (None)
             17 RETURN_VALUE        
>>> dis.dis(compile('{k: v for k, v in kv_pairs}', '', 'exec').co_consts[0])
  1           0 BUILD_MAP                0
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                21 (to 30)
              9 UNPACK_SEQUENCE          2
             12 STORE_FAST               1 (k)
             15 STORE_FAST               2 (v)
             18 LOAD_FAST                2 (v)
             21 LOAD_FAST                1 (k)
             24 MAP_ADD                  2
             27 JUMP_ABSOLUTE            6
        >>   30 RETURN_VALUE        
>>> dis.dis(compile('dict(kv_pairs)', '', 'exec'))
  1           0 LOAD_NAME                0 (dict)
              3 LOAD_NAME                1 (kv_pairs)
              6 CALL_FUNCTION            1
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE