这是一个人为设计的示例,用于演示在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']问题是:
a.列表理解是多次引用字典条目,还是引用并保留一个本地“临时”列表来处理?
b.在字典非常大的情况下,在同一字典项上包含多个条件句的最佳列表理解表达式是什么?
发布于 2012-07-02 20:34:47
您似乎只问了通用子表达式的优化。在您的列表理解中,它将多次索引到字典中。CPython是动态的,很难知道像Python这样的操作可能会有什么副作用,所以Python只是简单地执行您告诉它多次的操作。
其他实现,如PyPy,使用JIT并可能优化子表达式,但很难提前确定它将做什么。
如果您非常关心性能,则需要对不同的选项进行计时,以确定哪个选项最好。
发布于 2012-07-02 19:42:55
我不是研究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并比较两种实现(使用循环和列表组件),然后选择速度更快的实现。分析(一如既往)是您的朋友。
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 发布于 2012-07-02 19:44:03
第一点:这里没有“创建”任何东西(‘myseq’除外),既不是在forloop中,也不是在你的代码的listcomp版本中--它只是对现有dict项的引用。
现在来回答您的问题: list comp版本将进行查找(调用每个dict_index_mylists[i]表达式的dict.__getitem__。这些查找中的每一个都将返回对同一列表的引用。您可以通过保留对字典项的本地引用来避免这些额外的查找,例如:
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.itervalues()会更简单。
wrt/第二个问题,“最优”不是绝对的。您希望针对什么进行优化?太空?时间?可读性?
https://stackoverflow.com/questions/11292578
复制相似问题