Ответ 1
Версии Python до 2.7 и 3.1 использовали субоптимальный байт-код для создания понимания списка. В этих версиях Python понимание списка хранилось в локальной переменной (или даже глобальной, если в области модуля):
>>> import dis
>>> def foo():
... return [x for x in y]
...
>>> dis.dis(foo)
2 0 BUILD_LIST 0
3 DUP_TOP
4 STORE_FAST 0 (_[1])
7 LOAD_GLOBAL 0 (y)
10 GET_ITER
>> 11 FOR_ITER 13 (to 27)
14 STORE_FAST 1 (x)
17 LOAD_FAST 0 (_[1])
20 LOAD_FAST 1 (x)
23 LIST_APPEND
24 JUMP_ABSOLUTE 11
>> 27 DELETE_FAST 0 (_[1])
30 RETURN_VALUE
Локальная переменная _[1]
- это список в процессе. При понимании списка вложенности он будет использовать целые числа, чтобы ссылаться на результат:
>>> def bar():
... return [[x for x in y] for z in spam]
...
>>> dis.dis(bar)
2 0 BUILD_LIST 0
3 DUP_TOP
4 STORE_FAST 0 (_[1])
7 LOAD_GLOBAL 0 (spam)
10 GET_ITER
>> 11 FOR_ITER 40 (to 54)
14 STORE_FAST 1 (z)
17 LOAD_FAST 0 (_[1])
20 BUILD_LIST 0
23 DUP_TOP
24 STORE_FAST 2 (_[2])
27 LOAD_GLOBAL 1 (y)
30 GET_ITER
>> 31 FOR_ITER 13 (to 47)
34 STORE_FAST 3 (x)
37 LOAD_FAST 2 (_[2])
40 LOAD_FAST 3 (x)
43 LIST_APPEND
44 JUMP_ABSOLUTE 31
>> 47 DELETE_FAST 2 (_[2])
50 LIST_APPEND
51 JUMP_ABSOLUTE 11
>> 54 DELETE_FAST 0 (_[1])
57 RETURN_VALUE
Завершив цикл locals().values()
, вы включили ссылку на текущий список в возвращаемом значении. Обратите внимание, что байт-код использует DELETE_FAST
для очистки локального имени, чтобы попытаться избежать загрязнения пространства имен.
Это было оптимизировано для Python 3.1 и 2.7, см. issue 2183. Созданный результат списка был перемещен в стек. Оптимизация изменила байт-код LIST_APPEND
, чтобы указать, какой список в стеке добавить, устраняя необходимость использования DUP_TOP
→ STORE_FAST
в начале, LOAD_FAST
каждой итерации и DELETE_FAST
после понимания списка.