我指的是这个问题,特别是对@David Robinson和@mgilson的第一个答案的评论: Sum the second value of each tuple in a list
最初的问题是将每个tuble的第二个值相加:
structure = [('a', 1), ('b', 3), ('c', 2)]
第一回答:
sum(n for _, n in structure)
第二回答:
sum(x[1] for x in structure)
根据讨论,第一个答案要快50%。
一旦我弄明白第一个答案是什么(来自Perl,我用Google搜索了python中的特殊_变量意味着),我想知道怎样才会出现一个纯子集任务(只获得每个元组的第二个元素)与获取和绑定到变量两个元素)实际上更慢?是否缺少优化Python中的索引访问的机会?我错过了第二个答案需要时间吗?
答案 0 :(得分:35)
如果你看一下python字节码,很快就会明白为什么解包更快:
>>> import dis
>>> def unpack_or_index(t=(0, 1)):
... _, x = t
... x = t[1]
...
>>> dis.dis(unpack_or_index)
2 0 LOAD_FAST 0 (t)
3 UNPACK_SEQUENCE 2
6 STORE_FAST 1 (_)
9 STORE_FAST 2 (x)
3 12 LOAD_FAST 0 (t)
15 LOAD_CONST 1 (1)
18 BINARY_SUBSCR
19 STORE_FAST 2 (x)
22 LOAD_CONST 0 (None)
25 RETURN_VALUE
元组解包操作是一个简单的字节码(UNPACK_SEQUENCE
),而索引操作必须调用元组(BINARY_SUBSCR
)上的方法。解包操作可以在python评估循环中内联进行,而订阅调用需要使用PyObject_GetItem
查找元组对象上的函数以检索值。
UNPACK_SEQUENCE
opcode source code特殊情况下python元组或列表解包,其中序列长度与参数长度完全匹配:
if (PyTuple_CheckExact(v) &&
PyTuple_GET_SIZE(v) == oparg) {
PyObject **items = \
((PyTupleObject *)v)->ob_item;
while (oparg--) {
w = items[oparg];
Py_INCREF(w);
PUSH(w);
}
Py_DECREF(v);
continue;
} // followed by an "else if" statement for a list with similar code
上面的代码进入元组的本机结构并直接检索值;不需要使用诸如PyObject_GetItem
之类的大量调用,这些调用必须考虑到该对象可能是一个自定义的python类。
BINARY_SUBSCR
opcode仅针对python 列表进行了优化;任何不是本机python列表的内容都需要PyObject_GetItem
调用。
答案 1 :(得分:17)
索引通过__getitem__
特殊方法,因此必须为每个项执行函数查找和执行。这意味着,对于n
项列表,您最终会进行n
次查找/调用。
使用本机列表/元组时,解包不需要处理;它只是通过__iter__
这是一次调用,然后在C中解压缩结果序列。