Откуда интерпретатор CPython знает, как распечатать результат последнего выражения? - PullRequest
0 голосов
/ 16 мая 2018

Я копался в исходном коде, чтобы выяснить, в какой момент выводится результат.Например:

>>> x = 1
>>> x + 2
3

Два приведенных выше оператора скомпилированы в:

  1           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (x)
              6 LOAD_CONST               1 (None)
              9 RETURN_VALUE

и

  1           0 LOAD_NAME                0 (x)
              3 LOAD_CONST               0 (2)
              6 BINARY_ADD
              7 RETURN_VALUE

Первый оператор ничего не печатает, потому что None это возвращаемое значение.Вторая возвращает результат сложения.

Интерактивный цикл CPython вызывает PyRun_InteractiveOneObjectEx() для каждого входа. получает AST и вызывает от run_mod() до компиляции этого AST с байтовым кодом , а затем оценивает результат в виртуальной машине.Возвращенный объект Python, который получает PyRun_InteractiveOneObjectEx(), является просто вершиной стека виртуальной машины .

Пока что все это то, чего я ожидал.Но тогда возвращаемое значение кажется выброшенным !Когда это напечатано в REPL?

Кроме того, я вижу, что интерактивный режим меняет токенизатор;это invokes PyOS_Readline с подсказкой sys.ps1 (">>> " по умолчанию).Я проверил подобное изменение в pythonrun.c, но не повезло.

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Правильный ответ от @ jasonharper !Для потомков, здесь более глубокий взгляд на то, что происходит.

Вышеприведенная разборка показывает результаты режима eval:

>>> list(compile('x + 2', '<stdin>', 'eval').co_code)
[101, 0, 0, 100, 0, 0, 23, 83]

Код операции можно просмотреть с помощью:

>>> import dis
>>> dis.opname[101]
'LOAD_NAME'
>>> dis.opname[100]
'LOAD_CONST'
>>> dis.opname[23]
'BINARY_ADD'
>>> dis.opname[83]
'RETURN_VALUE'

Два числа, следующие за кодом операции, представляют 16-битный операнд, хотя нам нужен только первый байт.Так что это соответствует:

LOAD_NAME     0
LOAD_CONST    0
BINARY_ADD
RETURN_VALUE

Эти операнды являются индексами соответственно имен переменных и пула констант объекта скомпилированного кода.

>>> c = compile('x + 2', '<stdin>', 'eval')
>>> c.co_names
('x',)
>>> c.co_consts
(2,)

Пока это то, что мы имеем ввопрос.Но в действительности выполнение кода Python приводит к:

>>> list(compile('x + 2', '<stdin>', 'exec').co_code)
[101, 0, 0, 100, 0, 0, 23, 1, 100, 1, 0, 83]
>>> dis.opname[1]
'POP_TOP'

Т.е. результат отбрасывается, и в качестве возвращаемого значения вводится None.

В интерактивном режиме мы имеем:

>>> list(compile('x + 2', '<stdin>', 'single').co_code)
[101, 0, 0, 100, 0, 0, 23, 70, 100, 1, 0, 83]
>>> dis.opname[70]
'PRINT_EXPR'

Результат печатается (через sys.displayhook) и None становится фактическим возвращаемым значением.

Таким образом, печать вводится кодомфаза генерации, а не виртуальной машиной

0 голосов
/ 16 мая 2018

Вы показываете дизассемблирование байт-кода, созданного при наличии кода в функции. Это не то, как интерактивный код компилируется: он использует специальный «одиночный» режим (3-й параметр до compile(), если вы делали эквивалент в коде Python). В этом режиме код операции POP_TOP, который отбрасывает значение каждого выражения, вместо этого превращается в PRINT_EXPR. Причина, по которой x = 1 ничего не печатает, состоит в том, что операторы не оставляют в стеке ничего, что нужно вытолкнуть, поэтому это преобразование не применяется.

...