CPython: Почему для выполнения трехстрочного сценария требуется более 3 циклов в интерпретаторе? - PullRequest
0 голосов
/ 28 мая 2018

Я только что посмотрел эту лекцию на YouTube о CPython Internals от Филиппа Го, я озадачен одной вещью.

В 25:55 он изменяет источник C CPython, вставляя printf(“hello\n”) в начале бесконечного цикла, который выполняет все инструкции байтового кода;вы можете сделать то же самое:

  • Загрузка исходного кода Python 2.7 C
  • Открыть файл Python/ceval.c
  • Найти начало бесконечного цикла оценки,for (;;) {
  • Добавьте строку printf('hello\n'); в качестве первой строки бесконечного цикла.
  • Выполните configure и make для построения двоичного файла Python.

Он пишет в 3 строки test.py:

X = 1
Y = 2
print X + Y

Загадка в том, что когда он запускает test.py с модифицированным интерпретатором, почему так много «привет», прежде чем мы увидим «3«?

Этот трехстрочный код должен компилироваться в несколько инструкций с байтовым кодом, значением загрузки 1, значением загрузки 2 и инструкцией для вызова print, так что я представляю, когда речь идет о выполнении байтового кода, скомпилированного из теста.py, мы должны увидеть только несколько «привет».

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

1 Ответ

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

Есть две причины, по которым вы видите столько напечатанных hello:

  • В Python нет специального байт-кода для каждого возможного оператора Python.Вместо этого операторы будут использовать комбинацию байт-кодов.
  • Интерпретатор Python импортирует ряд модулей Python только для запуска .Вы можете запустить обычный интерпретатор Python с ключом -v, чтобы видеть, что импортируется каждый раз.Каждый модуль состоит из нескольких операторов, поэтому перед тем, как приступить к выполнению небольшого скрипта, нужно пройти через некоторый байт-код.

Если я добавлю эти 3 строки в test.py и использую мой неизмененныйБинарный Python 2.7 для запуска этого с переключателем -v, я вижу:

$ python2.7 -v test.py
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# /..../lib/python2.7/site.pyc matches /..../lib/python2.7/site.py
import site # precompiled from /..../lib/python2.7/site.pyc
# /..../lib/python2.7/os.pyc matches /..../lib/python2.7/os.py
import os # precompiled from /..../lib/python2.7/os.pyc
import errno # builtin
import posix # builtin
# /..../lib/python2.7/posixpath.pyc matches /..../lib/python2.7/posixpath.py
import posixpath # precompiled from /..../lib/python2.7/posixpath.pyc
# /..../lib/python2.7/stat.pyc matches /..../lib/python2.7/stat.py
import stat # precompiled from /..../lib/python2.7/stat.pyc
# /..../lib/python2.7/genericpath.pyc matches /..../lib/python2.7/genericpath.py
import genericpath # precompiled from /..../lib/python2.7/genericpath.pyc
# /..../lib/python2.7/warnings.pyc matches /..../lib/python2.7/warnings.py
import warnings # precompiled from /..../lib/python2.7/warnings.pyc
# /..../lib/python2.7/linecache.pyc matches /..../lib/python2.7/linecache.py
import linecache # precompiled from /..../lib/python2.7/linecache.pyc
# /..../lib/python2.7/types.pyc matches /..../lib/python2.7/types.py
import types # precompiled from /..../lib/python2.7/types.pyc
# /..../lib/python2.7/UserDict.pyc matches /..../lib/python2.7/UserDict.py
import UserDict # precompiled from /..../lib/python2.7/UserDict.pyc
# /..../lib/python2.7/_abcoll.pyc matches /..../lib/python2.7/_abcoll.py
import _abcoll # precompiled from /..../lib/python2.7/_abcoll.pyc
# /..../lib/python2.7/abc.pyc matches /..../lib/python2.7/abc.py
import abc # precompiled from /..../lib/python2.7/abc.pyc
# /..../lib/python2.7/_weakrefset.pyc matches /..../lib/python2.7/_weakrefset.py
import _weakrefset # precompiled from /..../lib/python2.7/_weakrefset.pyc
import _weakref # builtin
# /..../lib/python2.7/copy_reg.pyc matches /..../lib/python2.7/copy_reg.py
import copy_reg # precompiled from /..../lib/python2.7/copy_reg.pyc
import encodings # directory /..../lib/python2.7/encodings
# /..../lib/python2.7/encodings/__init__.pyc matches /..../lib/python2.7/encodings/__init__.py
import encodings # precompiled from /..../lib/python2.7/encodings/__init__.pyc
# /..../lib/python2.7/codecs.pyc matches /..../lib/python2.7/codecs.py
import codecs # precompiled from /..../lib/python2.7/codecs.pyc
import _codecs # builtin
# /..../lib/python2.7/encodings/aliases.pyc matches /..../lib/python2.7/encodings/aliases.py
import encodings.aliases # precompiled from /..../lib/python2.7/encodings/aliases.pyc
# /..../lib/python2.7/encodings/utf_8.pyc matches /..../lib/python2.7/encodings/utf_8.py
import encodings.utf_8 # precompiled from /..../lib/python2.7/encodings/utf_8.pyc
Python 2.7.15 (default, May  7 2018, 17:08:03)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
3
# -- clean-up output omitted --

Каждая строка import ... в них ссылается либо на встроенный модуль (часть двоичного кода Python, реализованного в C) или .pyc файл кэша байт-кода.17 таких файлов импортируются до того, как будет запущен даже код сценария .

3 строки кода в основном сценарии преобразуются в следующие 9 инструкций байт-кода:

>>> import dis
>>> dis.dis(compile(r'''\
... X = 1
... Y = 2
... print X + Y
... ''', '', 'exec'))
  2           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (X)

  3           6 LOAD_CONST               1 (2)
              9 STORE_NAME               1 (Y)

  4          12 LOAD_NAME                0 (X)
             15 LOAD_NAME                1 (Y)
             18 BINARY_ADD
             19 PRINT_ITEM
             20 PRINT_NEWLINE
             21 LOAD_CONST               2 (None)
             24 RETURN_VALUE

(я проигнорировал 2 байтовых кода в конце, кодируя дополнительные return None, которые на самом деле не применимы к модулю).

...