这是一个人为的例子,用于演示在for循环和列表理解中多次引用相同的字典项。首先,for循环:
dict_index_mylists = {0:['a', 'b', 'c'], 1:['b', 'c', 'a'], 2:['c', 'a', 'b']}
# for-loop
myseq = []
for i in [0, 1, 2]:
interim = dict_index_mylists[i]
if interim[0] == 'b' or interim[1] == 'c' or interim[2] == 'a':
myseq.append(interim)
在for循环中,临时列表从字典对象引用,然后在if条件中多次引用,这可能是有意义的,特别是如果字典非常大和/或存储。然后,'临时'引用可能是不必要的,因为Python字典已针对性能进行了优化。
这是对for循环的列表理解:
# list-comprehension
myseq = [dict_index_mylists[i] for i in [0, 1, 2] if dict_index_mylists[i][0] == 'b' or dict_index_mylists[i][1] == 'c' or dict_index_mylists[i][2] == 'a']
问题是:
一个。 list-comprehension是否对字典项进行了多次引用,还是引用并保留了本地“临时”列表?
湾什么是最佳列表推导表达式,它在同一个字典项上包含多个条件,并且字典非常大?
答案 0 :(得分:1)
您似乎只询问常见子表达式的优化。在列表理解中,它将多次索引到字典中。 Python是动态的,很难知道像dict_index_mylists[i]
这样的操作会产生什么样的副作用,所以CPython只是简单地执行操作。
像PyPy这样的其他实现使用JIT并且可以优化子表达式,但很难确切知道它将提前做什么。
如果您非常关注性能,则需要考虑各种选项以确定哪种方法最佳。
答案 1 :(得分:0)
我不是查看python字节码的专家,但这是我今天早上学习新东西的尝试:
def dostuff():
myseq = [dict_index_mylists[i] for i in [0, 1, 2] if dict_index_mylists[i][0] == 'b' or dict_index_mylists[i][1] == 'c' or dict_index_mylists[i][2] == 'a']
import dis
dis.dis(dostuff)
如果你查看输出(下面),有4次调用LOAD_GLOBAL
,所以它看起来不像python存储临时列表。至于你的第二个问题,你所拥有的可能与你能做的一样好。它并没有你想象的那么糟糕。 dict
对象通过散列函数访问项目,因此无论字典大小如何,它们的查找复杂度为O(1)
。当然,你总是可以使用timeit
并比较两个实现(使用loop和list-comp),然后选择更快的实现。分析(一如既往)是你的朋友。
APENDIX(dis.dis(dostuff)的输出)
5 0 BUILD_LIST 0
3 DUP_TOP
4 STORE_FAST 0 (_[1])
7 LOAD_CONST 1 (0)
10 LOAD_CONST 2 (1)
13 LOAD_CONST 3 (2)
16 BUILD_LIST 3
19 GET_ITER
>> 20 FOR_ITER 84 (to 107)
23 STORE_FAST 1 (i)
26 LOAD_GLOBAL 0 (dict_index_mylists)
29 LOAD_FAST 1 (i)
32 BINARY_SUBSCR
33 LOAD_CONST 1 (0)
36 BINARY_SUBSCR
37 LOAD_CONST 4 ('b')
40 COMPARE_OP 2 (==)
43 JUMP_IF_TRUE 42 (to 88)
46 POP_TOP
47 LOAD_GLOBAL 0 (dict_index_mylists)
50 LOAD_FAST 1 (i)
53 BINARY_SUBSCR
54 LOAD_CONST 2 (1)
57 BINARY_SUBSCR
58 LOAD_CONST 5 ('c')
61 COMPARE_OP 2 (==)
64 JUMP_IF_TRUE 21 (to 88)
67 POP_TOP
68 LOAD_GLOBAL 0 (dict_index_mylists)
71 LOAD_FAST 1 (i)
74 BINARY_SUBSCR
75 LOAD_CONST 3 (2)
78 BINARY_SUBSCR
79 LOAD_CONST 6 ('a')
82 COMPARE_OP 2 (==)
85 JUMP_IF_FALSE 15 (to 103)
>> 88 POP_TOP
89 LOAD_FAST 0 (_[1])
92 LOAD_GLOBAL 0 (dict_index_mylists)
95 LOAD_FAST 1 (i)
98 BINARY_SUBSCR
99 LIST_APPEND
100 JUMP_ABSOLUTE 20
>> 103 POP_TOP
104 JUMP_ABSOLUTE 20
>> 107 DELETE_FAST 0 (_[1])
110 STORE_FAST 2 (myseq)
113 LOAD_CONST 0 (None)
116 RETURN_VALUE
答案 2 :(得分:0)
第一点:没有(期望'myseq')在这里“创建”,既不在forloop也不在listcomp版本的代码中 - 它只是对现有dict项的引用。
现在回答你的问题:list comp版本将进行查找(对每个dict.__getitem__
表达式调用dict_index_mylists[i]
。每个查找将返回对同一列表的引用你可以通过保留对dict项目的本地引用来避免这些额外的查找,即:
myseq = [
item for item in (dict_index_mylists[i] for i in (0, 1, 2))
if item[0] == 'b' or item[1] == 'c' or item[2] == 'a'
]
但是为了编写listcomp而编写listcomp没有意义。
请注意,如果您不关心原始排序并希望将其应用于整个dict,则使用dict.itervalues()
会更简单。