Этот код Python:
import dis
def f():
a=[1,2,3]
dis.dis(f)
генерирует этот вывод:
2 0 RESUME 0
3 2 BUILD_LIST 0
4 LOAD_CONST 1 ((1, 2, 3))
6 LIST_EXTEND 1
8 STORE_FAST 0 (a)
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
Меня смущает процесс построения массива.
Я предполагаю, что:
BUILD_LIST 0
устанавливает маркер или, может быть, заполнитель для указателя списка?.LOAD_CONST 1
помещает все элементы константы №1 (это тот самый кортеж) по одному от последнего к первому в машинный стек.LIST_EXTEND 1
выскакивает и добавляет в список по одному элементу из машинного стека за раз, пока не достигнет маркера (я не знаю, для чего нужен 1
).STORE_FAST
теперь у нас есть указатель сверху, поэтому эта инструкция окончательно привязывает указатель к идентификатору #0, которым оказывается a
.Это верно? (редактировать: нет, предположение неверно)
о, так то, что попадает в стек, на самом деле является указателем на константу кортежа, верно?
Пожалуйста, не используйте <strike>
, чтобы показать, что ваше первоначальное понимание неверно. Вместо этого напишите ответ.
«Редактировать» не намного лучше. См., например. Что мне следует избегать в своих постах и заголовках? (возле «Журналы изменений»). Вопрос должен выглядеть так, как будто он был написан прямо сейчас (без аннулирования ответов).
Cpython выполняет свои байт-коды в виртуальной машине на основе стека.
Теперь давайте начнем построчно объяснять вывод dis
.
BUILD_LIST 0
Здесь BUILD_LIST
— это opcode
, а 0
— это oparg
. Это создаст пустой список и поместит его на вершину стека.
case TARGET(BUILD_LIST): {
PyObject *list = PyList_New(oparg); // create empty list
if (list == NULL)
goto error;
// Skipped other parts of the code for brevity.
PUSH(list); // push it onto the top of the stack
DISPATCH();
}
После этой операции состояние стека будет
LOAD_CONST 1
Это переместит (1, 2, 3) (т. е. co_consts[1]) на вершину стека.
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
DISPATCH();
}
состояние стека теперь изменилось на
LIST_EXTEND 1
Сначала это будет pop
итерация из
вершина стопки (это (1, 2, 3)
).
PEEK(oparg)
затем получит oparg
-й (то есть 1) элемент стека, не удаляя его (это объект list
).
PEEK(n)
расширяется до #define PEEK(n) (stack_pointer[-(n)])
case TARGET(LIST_EXTEND): {
PyObject *iterable = POP();
PyObject *list = PEEK(oparg);
PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
// Skipped other parts of the code for brevity.
Py_DECREF(none_val);
Py_DECREF(iterable);
DISPATCH();
}
API _PyList_Extend
расширяет список за счет итерации на месте.
Теперь состояние стека будет таким:
STORE_FAST
Сохраняет STACK.pop() в локальную переменную a(co_varnames\[var_num\]).
Спасибо за подробный ответ. Содержит ли стек PVM что-либо, кроме указателей или базовых типов, таких как целые числа?
значит, размер каждого элемента в стеке PVM является переменным? (один элемент может содержать целый кортеж)