Все объекты в co_consts
являются константами, т.е. они неизменны. Вы не должны иметь возможность, например, добавлять в список, который выглядит как литерал в исходном коде, и, таким образом, изменять поведение функции.
Компилятор обычно представляет литералы списка, перечисляя все отдельные константы, появляющиеся в список:
>>> def f():
... a = [1, 2, 3]
... return 1 in a
...
>>> f.__code__.co_consts
(None, 1, 2, 3)
Глядя на байт-код этой функции, мы видим, что функция строит список во время выполнения каждый раз, когда функция выполняется:
>>> dis.dis(f)
2 0 LOAD_CONST 1 (1)
2 LOAD_CONST 2 (2)
4 LOAD_CONST 3 (3)
6 BUILD_LIST 3
8 STORE_FAST 0 (a)
3 10 LOAD_CONST 1 (1)
12 LOAD_FAST 0 (a)
14 COMPARE_OP 6 (in)
16 RETURN_VALUE
Создание В общем случае требуется новый список, поскольку функция может изменять или возвращать список, определенный литералом, и в этом случае ей нужно работать с новым объектом списка каждый раз, когда выполняется функция.
В других контекстах, однако создание нового объекта списка расточительно. По этой причине оптимизатор глазков Python может заменить список кортежем или набором frozen_set
в определенных ситуациях, когда известно, что он безопасен. Одна из таких ситуаций - когда литерал списка или набора используется только в выражении вида x [not] in <list_literal>
. Еще одна такая ситуация - когда литерал списка используется в for
l oop.
Оптимизатор глазка очень прост. Это только смотрит на одно выражение за один раз. По этой причине он не может обнаружить, что эта оптимизация будет безопасной в моем определении f
выше, что функционально эквивалентно вашему примеру.