Процесс Компиляции/Интерпретации Python

Я пытаюсь понять процесс компилятора/интерпретатора Python более ясно. К сожалению, я не посещал урок в интерпретаторах, и при этом я не читал очень о них.

В основном то, что я понимаю прямо сейчас, - то, что код Python из .py файлов сначала компилируется в байт-код Python (который я принимаю, .pyc файлы, которые я иногда вижу?). Затем, байт-код компилируется в машинный код, язык, который на самом деле понимает процессор. В значительной степени я считал этот поток Почему компиляция Python источник к байт-коду перед интерпретацией?

Кто-то мог дать мне хорошее объяснение целого процесса, имеющего в виду, что мое знание компиляторов/интерпретаторов почти не существует? Или, если это не возможно, возможно, дайте мне некоторые ресурсы, которые дают быстрые обзоры компиляторов/интерпретаторов?

Спасибо

41
задан Community 23 May 2017 в 11:54
поделиться

1 ответ

Байт-код на самом деле не интерпретируется в машинный код, если вы не используете какую-то экзотическую реализацию, такую ​​как pypy.

В остальном у вас правильное описание. Байт-код загружается в среду выполнения Python и интерпретируется виртуальной машиной, которая представляет собой фрагмент кода, который считывает каждую инструкцию в байт-коде и выполняет любую указанную операцию. Вы можете увидеть этот байт-код в модуле dis следующим образом:

>>> def fib(n): return n if n < 2 else fib(n - 2) + fib(n - 1)
... 
>>> fib(10)
55
>>> import dis
>>> dis.dis(fib)
  1           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (2)
              6 COMPARE_OP               0 (<)
              9 JUMP_IF_FALSE            5 (to 17)
             12 POP_TOP             
             13 LOAD_FAST                0 (n)
             16 RETURN_VALUE        
        >>   17 POP_TOP             
             18 LOAD_GLOBAL              0 (fib)
             21 LOAD_FAST                0 (n)
             24 LOAD_CONST               1 (2)
             27 BINARY_SUBTRACT     
             28 CALL_FUNCTION            1
             31 LOAD_GLOBAL              0 (fib)
             34 LOAD_FAST                0 (n)
             37 LOAD_CONST               2 (1)
             40 BINARY_SUBTRACT     
             41 CALL_FUNCTION            1
             44 BINARY_ADD          
             45 RETURN_VALUE        
>>> 

Подробное объяснение

Очень важно понимать, что приведенный выше код никогда не выполняется вашим процессором; при этом он никогда не конвертируется во что-то, что есть (по крайней мере, не в официальной реализации Python на языке C). ЦП выполняет код виртуальной машины, который выполняет работу, указанную инструкциями байт-кода. Когда интерпретатор хочет выполнить функцию fib , он читает инструкции по одной за раз и выполняет то, что они ему приказывают. Он просматривает первую инструкцию, LOAD_FAST 0 , и, таким образом, захватывает параметр 0 ( n , переданный в fib ) из того места, где хранятся параметры, и помещает его в стек интерпретатора (интерпретатор Python - это стековая машина).При чтении следующей инструкции LOAD_CONST 1 она берет константу с номером 1 из набора констант, принадлежащих функции, которая в данном случае является номером 2, и помещает ее в стек. Фактически вы можете увидеть эти константы:

>>> fib.func_code.co_consts
(None, 2, 1)

Следующая инструкция, COMPARE_OP 0 , сообщает интерпретатору, что нужно выделить два самых верхних элемента стека и выполнить сравнение неравенства между ними, поместив логический результат обратно в стек. Четвертая инструкция определяет на основе логического значения, следует ли перейти на пять инструкций вперед или продолжить выполнение следующей инструкции. Все это словоблудие объясняет if n <2 часть условного выражения в fib . Это будет очень поучительное упражнение, чтобы разобраться в значении и поведении остальной части байт-кода fib . Единственное, в чем я не уверен, - это POP_TOP ; Я предполагаю, что JUMP_IF_FALSE определен, чтобы оставить свой логический аргумент в стеке, а не выталкивать его, поэтому он должен быть извлечен явно.

Еще более поучительно проверить необработанный байт-код для fib следующим образом:

>>> code = fib.func_code.co_code
>>> code
'|\x00\x00d\x01\x00j\x00\x00o\x05\x00\x01|\x00\x00S\x01t\x00\x00|\x00\x00d\x01\x00\x18\x83\x01\x00t\x00\x00|\x00\x00d\x02\x00\x18\x83\x01\x00\x17S'
>>> import opcode
>>> op = code[0]
>>> op
'|'
>>> op = ord(op)
>>> op
124
>>> opcode.opname[op]
'LOAD_FAST'
>>> 

Таким образом, вы можете увидеть, что первый байт байт-кода - это инструкция LOAD_FAST . Следующая пара байтов, '\ x00 \ x00' (число 0 в 16 битах) является аргументом LOAD_FAST и сообщает интерпретатору байт-кода загрузить параметр 0 в стек. .

53
ответ дан 27 November 2019 в 00:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: