13

Если Python интерпретируемый, то что такие файлы .pyc?

11

Я использую интерпретируемый язык Python, но заметил, что в моей директории с исходным кодом появились файлы с расширением .pyc, которые Windows определяет как "Скомпилированные файлы Python". Почему это происходит и каковы причины появления этих файлов в моем проекте?

5 ответ(ов)

11

Данное популярное утверждение о том, что Python является интерпретируемым языком, является неверным или, скорее, основано на неправильном понимании уровней (естественных) языков. Подобная ошибка была бы, например, сказать, что "Библия — это книга в твердом переплете". Позвольте мне объяснить эту аналогию...

"Библия" — это "книга" в том смысле, что она представляет собой класс объектов (физических объектов, идентифицированных как книги); книги, обозначенные как "копии Библии", предполагается, что должны иметь нечто общее (содержание, хотя даже они могут быть на разных языках с различными допустимыми переводами и уровнями аннотаций). Тем не менее, эти книги могут различаться во множестве аспектов, которые не считаются фундаментальными — тип переплета, цвет переплета, шрифты, использованные при печати, иллюстрации (если есть), наличие широких полей для записей и так далее.

Вполне возможно, что типичная печать Библии действительно будет в твердом переплете — в конце концов, это книга, которая обычно предназначена для многократного чтения, с закладками на нескольких страницах, которая перелистывается в поисках конкретных глав и стихов и так далее. Хороший переплет может сделать экземпляр более долговечным при таком использовании. Однако это тривиальные (практические) моменты, которые не могут использоваться для определения, является ли конкретный физический объект копией Библии или нет: печатные издания в мягком переплете абсолютно возможны!

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

Классическая реализация, CPython, часто просто называется "Python" для краткости — но она лишь одна из нескольких высококачественных реализаций наряду с IronPython от Microsoft (которая компилирует в коды CLR, т.е., ".NET"), Jython (которая компилирует в коды JVM) и PyPy (которая написана на самом Python и может компилировать в огромное множество "бэкенд" форм, включая сгенерированный "на лету" машинный код). Все они являются реализациями Python ("реализации языка Python"), как и многие визуально разные объекты могут быть Библиями ("копиями Библии").

Если вас интересует именно CPython: он компилирует исходные файлы в нижний уровень, специфичный для Python (известный как "байт-код"), и делает это автоматически по мере необходимости (когда нет соответствующего файла байт-кода или файл старше исходного или был скомпилирован другой версией Python), обычно сохраняет файлы байт-кода на диск, чтобы избежать повторной компиляции в будущем. С другой стороны, IronPython обычно компилирует в коды CLR (сохраняет их на диск или нет, в зависимости от ситуации), а Jython — в коды JVM (также может сохранять или не сохранять их).

Эти более низкие уровни затем выполняются соответствующими "виртуальными машинами", также известными как "интерпретаторы" — виртуальная машина CPython, среда .Net, Java VM (также известная как JVM) и т.д.

Так что в этом смысле (что делают типичные реализации) Python является "интерпретируемым языком" только если C# и Java тоже являются таковыми: все они имеют типичную стратегию реализации, заключающуюся в том, чтобы сначала производить байт-код, а затем выполнять его через виртуальную машину/интерпретатор.

Скорее всего, основной фокус заключается в том, насколько "тяжелым", медленным и формальным является процесс компиляции. CPython разрабатывается так, чтобы компилировать как можно быстрее, с минимальной нагрузкой, с как можно меньшим количеством формальностей — компилятор выполняет очень немного проверок на ошибки и оптимизаций, что позволяет ему работать быстро и потреблять мало памяти, что, в свою очередь, позволяет запускать его автоматически и прозрачно, когда это необходимо, без необходимости сообщать пользователю о том, что происходит компиляция, большую часть времени. Java и C# обычно принимают больше работы во время компиляции (и, следовательно, не выполняют автоматическую компиляцию), чтобы более тщательно проверять ошибки и выполнять больше оптимизаций. Это континуум серых оттенков, а не черно-белая ситуация, и было бы совершенно произвольно установить порог на каком-то уровне и сказать, что только выше этого уровня можно называть "компиляцией"!

2

Вопрос о том, существует ли такой термин, как "интерпретируемый язык", часто приводит к недоразумениям. На самом деле, использование интерпретатора или компилятора является характеристикой конкретной реализации языка и никак не связано с самим языком.

Каждый язык может быть реализован как интерпретатором, так и компилятором. Большинство языков имеют по крайней мере одну реализацию каждого типа. Например, существуют интерпретаторы для C и C++, а также компиляторы для JavaScript, PHP, Perl, Python и Ruby. Более того, большинство современных реализаций языков фактически объединяют в себе и интерпретатор, и компилятор (или даже несколько компиляторов).

Язык — это всего лишь набор абстрактных математических правил. Интерпретатор — это одна из нескольких реальных стратегий реализации языка. Эти два понятия находятся на совершенно разных уровнях абстракции. Если бы английский язык был типизированным, использование термина "интерпретируемый язык" можно было бы считать ошибкой типа. Утверждение "Python — это интерпретируемый язык" неверно не только в том смысле, что оно не соответствует действительности (потому что быть неверным подразумевает, что это утверждение имеет смысл, даже если оно неправильно), оно просто лишено всякого смысла, поскольку язык никогда не может быть определен как "интерпретируемый".

Если рассмотреть существующие реализации Python, то можно выделить следующие стратегии реализации:

  • IronPython: компилирует в деревья DLR, которые затем компилируются в байт-код CIL. То, что происходит с байт-кодом CIL, зависит от используемой платформы CLI, например, Microsoft .NET, GNU Portable.NET и Novell Mono компилируют его в нативный машинный код.
  • Jython: интерпретирует исходный код Python, пока не обнаружит горячие пути кода, которые затем компилируются в байт-код JVML. Поведение JVML байт-кода зависит от работающей JVM. Maxine прямо компилирует его в нативный код без оптимизации, пока не обнаружит горячие пути, которые затем компилируются в оптимизированный нативный код. HotSpot сначала интерпретирует JVML байт-код, а затем компилирует горячие пути в оптимизированный машинный код.
  • PyPy: компилирует в байт-код PyPy, который затем интерпретируется виртуальной машиной PyPy, пока не будет найдено горячее кодовое выполнение, которое затем компилируется в нативный код, байт-код JVML или байт-код CIL в зависимости от используемой платформы.
  • CPython: компилирует в байт-код CPython, который затем интерпретируется.
  • Stackless Python: компилирует в байт-код CPython, который затем интерпретируется.
  • Unladen Swallow: компилирует в байт-код CPython, который затем интерпретируется, пока не будут найдены горячие пути, которые затем компилируются в LLVM IR и компилируются в нативный машинный код.
  • Cython: компилирует код на Python в переносимый C-код, который затем компилируется с помощью стандартного C-компилятора.
  • Nuitka: компилирует код на Python в машиннозависимый C++ код, который затем компилируется со стандартным C-компилятором.

Вы можете заметить, что каждая из перечисленных реализаций (и некоторые другие, такие как tinypy, Shedskin или Psyco) включает в себя компилятор. Насколько мне известно, на данный момент не существует реализации Python, которая была бы чисто интерпретируемой; такой реализации не планируется, и никогда не существовало подобной реализации.

Следовательно, термин "интерпретируемый язык" не имеет смысла. Даже если его понимать как "язык с интерпретируемой реализацией", это также явно не соответствует действительности. Тот, кто вам это сказал, очевидно, не знает, о чем говорит.

В частности, файлы .pyc, которые вы наблюдаете, представляют собой кэшированные файлы байт-кода, создаваемые CPython, Stackless Python или Unladen Swallow.

0

.pyc файлы создаются интерпретатором Python при импорте .py файла. Они содержат "собранный байт-код" импортированного модуля или программы. Идея заключается в том, что "перевод" из исходного кода в байт-код (который нужно выполнить только один раз) может быть пропущен при последующих импортированиях, если .pyc файл новее соответствующего .py файла, что немного ускоряет запуск. Однако, интерпретация все равно осуществляется.

0

ЭТО ДЛЯ НАЧИНАЮЩИХ,

Python автоматически компилирует ваш скрипт в скомпилированный код, так называемый байт-код, перед его выполнением.

Запуск скрипта не считается импортом, и файл .pyc не будет создан.

Например, если у вас есть файл скрипта abc.py, который импортирует другой модуль xyz.py, когда вы запускаете abc.py, будет создан xyz.pyc, поскольку модуль xyz импортируется, но файл abc.pyc не будет создан, так как abc.py не импортируется.

Если вам нужно создать файл .pyc для модуля, который не импортируется, вы можете использовать модули py_compile и compileall.

Модуль py_compile может вручную скомпилировать любой модуль. Один из способов — использовать функцию py_compile.compile в этом модуле интерактивно:

>>> import py_compile
>>> py_compile.compile('abc.py')

Это создаст файл .pyc в том же месте, что и abc.py (вы можете переопределить это с помощью необязательного параметра cfile).

Вы также можете автоматически скомпилировать все файлы в директории или директориях, используя модуль compileall.

python -m compileall

Если имя директории (текущая директория в этом примере) опущено, модуль компилирует все, что находится в sys.path.

0

Python (по крайней мере, его самая распространенная реализация) использует подход, при котором исходный код компилируется в байт-код, а затем этот байт-код интерпретируется виртуальной машиной. Это означает, что, как правило, он не является чисто интерпретируемым языком или чисто компилируемым.

Однако следует отметить, что процесс компиляции в большинстве случаев скрыт от пользователя – файлы .pyc в основном рассматриваются как кэш; они ускоряют выполнение, но обычно вам не нужно о них беспокоиться. Python автоматически аннулирует и перезагружает их (повторно компилирует исходный код) при необходимости, основываясь на метках времени/дат файлов.

Всё, что я могу отметить как проблему в этом процессе, – это когда скомпилированный файл байт-кода по каким-то причинам получил метку времени в будущем, что означало, что он всегда выглядел новее, чем исходный файл. Поскольку он выглядел новее, исходный файл никогда не компилировался заново, и, как бы вы ни изменяли его, изменения игнорировались...

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь