Почему locals() возвращают странный собственный референтный список?

Поэтому я использую locals(), чтобы захватить некоторые аргументы в функции. Хорошо работает:

def my_function(a, b):
    print locals().values()

>>> my_function(1,2)
[1, 2]

Стандартный материал. Но теперь давайте представим понимание списка:

def my_function(a, b):
    print [x for x in locals().values()]

>>> my_function(1,2)
[[...], 1, 2]

Эхх? Почему он вставил саморекламу?

Ответы

Ответ 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_TOPSTORE_FAST в начале, LOAD_FAST каждой итерации и DELETE_FAST после понимания списка.