Почему Python не нужен компилятор?

29

Просто интересно (теперь, когда я начал с C ++, которому нужен компилятор), почему Python не нужен компилятор?

Я просто ввожу код, сохраняю его как exec и запускаю. В C ++ я должен делать сборки и все такое прочее.

Billjk
источник
4
Python - это просто язык с множеством реализаций. Iron Python компилируется так же, как C # и C ++, и могут быть другие реализации, подобные этому.
Работа
1
C # и C ++ не скомпилированы одинаково - хотя вы можете утверждать, что в конечном итоге они оба становятся машинными инструкциями, но если вы это сделаете, вы можете сказать, что BASIC также скомпилирован одинаково.
gbjbaanb
7
@gbjbaanb, но опять же английский язык не компилируется, и семантический анализ одного предложения может дать два одинаково правильных результата, и вышеприведенное можно прочитать как «железный питон скомпилирован так же, как C # и C ++»
Rune FS
Какую платформу / программное обеспечение вы используете для написания кода Python? Если вы пишете .py файл, это не исполняемый файл. Это все еще файл исходного кода. Из командной строки вы используете pythonкоманду для интерпретации файла .py или, если вы используете IDLE или Eclipse, IDE сделает это за вас.
Рик Хендерсон

Ответы:

68

У Python есть компилятор! Вы просто не замечаете этого, потому что он запускается автоматически. Однако вы можете сказать, что он там: посмотрите на .pyc(или, .pyoесли у вас включен оптимизатор) файлы, сгенерированные для ваших модулей import.

Кроме того, он не компилируется в код родной машины. Вместо этого он компилируется в байт-код, который используется виртуальной машиной. Виртуальная машина сама является скомпилированной программой. Это очень похоже на работу Java; настолько похоже, что фактически существует вариант Python ( Jython ), который вместо этого компилируется в байт-код виртуальной машины Java! Есть также IronPython , который компилируется в CLR от Microsoft (используется .NET). (Обычный компилятор байт-кода Python иногда называется CPython для устранения неоднозначности этого варианта.)

C ++ должен раскрыть процесс компиляции, потому что сам язык неполон; он не определяет все, что должен знать компоновщик для сборки вашей программы, и не может указывать параметры компиляции переносимо (некоторые компиляторы позволяют вам использовать #pragma, но это не стандартно). Таким образом, вы должны выполнить остальную часть работы с make-файлами и, возможно, с auto hell (autoconf / automake / libtool). Это действительно просто пережиток того, как С это сделал. И C сделал это таким образом, потому что это сделало компилятор простым, что является одной из основных причин его популярности (любой мог запустить простой C-компилятор в 80-х годах).


Некоторые вещи, которые могут повлиять на работу компилятора или компоновщика, но не указаны в синтаксисе C или C ++:

  • разрешение зависимостей
  • требования к внешней библиотеке (включая порядок зависимостей)
  • уровень оптимизатора
  • настройки предупреждений
  • версия спецификации языка
  • сопоставления компоновщика (какой раздел находится в финальной программе)
  • целевая архитектура

Некоторые из них могут быть обнаружены, но не могут быть указаны; например, я могу определить, с каким C ++ используется __cplusplus, но я не могу указать, что C ++ 98 - это тот, который используется для моего кода в самом коде; Я должен передать его как флаг компилятору в Makefile или сделать настройку в диалоге.

Хотя вы можете подумать, что в компиляторе существует система «разрешения зависимостей», автоматически генерирующая записи зависимостей, эти записи только говорят, какие заголовочные файлы использует данный исходный файл. Они не могут указать, какие дополнительные модули исходного кода требуются для связи в исполняемой программе, поскольку в C или C ++ нет стандартного способа указать, что данный заголовочный файл является определением интерфейса для другого модуля исходного кода, а не просто набором линии, которые вы хотите показать в нескольких местах, чтобы не повторяться. Существуют традиции в соглашениях об именах файлов, но они не известны или не применяются компилятором и компоновщиком.

Некоторые из них могут быть установлены с использованием #pragma, но это нестандартно, и я говорил о стандарте. Все эти вещи могут быть определены стандартом, но не в интересах обратной совместимости. Преобладающая мудрость заключается в том, что make-файлы и IDE не сломаны, поэтому не исправляйте их.

Python обрабатывает все это на языке. Например, importуказывает явную зависимость модуля, подразумевает дерево зависимостей, а модули не разделяются на заголовочные и исходные файлы (т. Е. Интерфейс и реализацию).

Майк ДеСимоне
источник
3
Реализация Python на C - это CPython , Cython - это нечто другое.
Грег Хьюгилл
4
Другие причины, по которым C компилировался в машинный код, заключались в том, что он должен был быть чуть более чем прославленным ассемблером, потому что интерпретаторы байт-кода были технически невозможны на имеющемся у них оборудовании, а также потому, что одной из наиболее важных задач было написание ядра ОС.
tdammers
2
@BillyONeal с одним большим исключением, что в c / c ++ вы, как программист, должны делать вещи определенным образом (либо создавать файлы, либо помещать все в один и тот же BLOB-объект) в Python, вы просто выполняете свою работу и компилятор вместе с виртуальной машиной. заботится об остальном
Rune FS
3
«C ++ нужно раскрыть процесс компиляции, потому что сам язык неполон» Э-э, что ??
Легкость гонок с Моникой
3
Вы прочитали часть сразу после этого , верно? «в нем не указано все, что нужно знать компоновщику для сборки вашей программы, и при этом он не может указывать параметры компиляции». Вы не можете просто создать любой файл C ++, передав его компилятору; часто вы должны предоставлять метаданные, такие как флаги компиляции, включать пути и т. д. Эти метаданные не определены стандартом и не являются переносимыми, поэтому нам приходится перетаскивать другие вещи, такие как make, cmake, Visual Studio или что-то еще, чтобы закончить работу Таким образом, стандарт должен ссылаться на некоторые вещи, как в модуле компиляции, а другие на всю программу.
Майк ДеСимон
7

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

Когда такой язык, как C ++ (скомпилированный язык), компилируется, это означает, что он преобразуется в машинный код для непосредственного чтения аппаратным обеспечением при выполнении. Статья Википедии на скомпилированных языках может дать интересный контраст.

Зенон
источник
21
Нет такой вещи как интерпретируемый или скомпилированный язык. Язык - это абстрактный набор математических правил. Язык не компилируется и не интерпретируется. Язык просто есть . Компиляция и интерпретация - это черты компилятора или интерпретатора (дух!), А не языка. Каждый язык может быть реализован с помощью компилятора, и каждый язык может быть реализован с помощью интерпретатора. Большинство языков имеют как скомпилированные, так и интерпретированные реализации. Есть интерпретаторы для C ++ и есть компиляторы для Python. (Фактически, все существующие в настоящее время реализации Python имеют компиляторы.)
Jörg W Mittag
4
Большинство современных высокопроизводительных языковых реализаций сочетают в себе интерпретатор и компилятор (или даже несколько компиляторов) для максимальной производительности. На самом деле, невозможно запустить любую программу без переводчика. В конце концов, компилятор - это просто программа, которая переводит программу с одного языка на другой. Но в какой-то момент вы фактически должны запустить программу, которая выполняется интерпретатором (который может или не может быть реализован в кремнии).
Йорг Миттаг
10
@ JörgWMittag: Вы технически правы. Однако большинство языков были разработаны для интерпретированного контекста или для полной компиляции. Написание интерпретатора для GW BASIC или Common Lisp намного проще, чем для, скажем, C ++ или C #; Python теряет многие свои торговые точки без интерактивной среды; написание компилятора для PHP чертовски сложно и, возможно, ужасно неэффективно, поскольку скомпилированный исполняемый файл должен содержать весь интерпретатор PHP из-за eval () и подобных конструкций - можно утверждать, что такой компилятор будет обманывать.
tdammers
2
@tdammers, да. Мы можем разумно использовать «скомпилированный язык» для обозначения «обычно скомпилированного языка». Но это упускает из виду тот факт, что PHP, Java, Python, Lua и C # все реализованы как компиляторы для байт-кода. На всех этих языках также были реализованы JIT. На самом деле, вы не можете назвать некоторые из этих языков скомпилированными, а некоторые интерпретировать, потому что у них одинаковая стратегия реализации.
Уинстон Эверт
2
@BillyONeal, по крайней мере, для python. Вы можете распространять байт-код Python и запускать его без исходного кода. Но верно, что вы не можете распространять python без компилятора.
Уинстон Эверт
5

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

То, с чем вы сталкиваетесь, является особенностью / ограничением C ++ (или, по крайней мере, реализаций C ++).

Чтобы что-то сделать, вы должны хранить свой код в файлах и создавать монолитное изображение с помощью процесса, называемого связыванием.

В частности, именно этот монолитный процесс связывания ошибочно принимается за различие между компиляцией и интерпретацией.

Некоторые языки делают все это гораздо более динамично, устраняя неуклюжий шаг монолитного связывания, а не компилируя в машинный код. Источник по-прежнему компилируется в объектные файлы, но они загружаются в образ времени выполнения, а не связаны в монолитный исполняемый файл.

Вы говорите «перезагрузите этот модуль», и он загружает исходный код и интерпретирует его, или компилирует, в зависимости от переключателя режима.

Программирование в ядре Linux имеет некоторые особенности, даже если вы работаете в C. Вы можете перекомпилировать модуль, загрузить и выгрузить его. Конечно, вы все еще знаете, что вы производите какую-то исполняемую вещь, и она управляется сложной системой сборки, с некоторыми ручными шагами. Но дело в том, что, в конце концов, вы можете выгрузить и перезагрузить только этот маленький модуль и не нужно перезагружать все ядро.

Некоторые языки имеют даже более тонкую модульность, чем эта, и сборка и загрузка выполняются изнутри времени их выполнения, поэтому она более плавная.

Kaz
источник
2

что отвлекает от первоначального вопроса ... Не упоминается то, что источником программы на Python является то, что вы используете и распространяете, с точки зрения пользователя, это программа. Мы склонны упрощать вещи в категории, которые не определены четко.

Скомпилированные программы обычно рассматриваются как отдельные файлы машинного кода. (по общему признанию часто содержащий ссылки на динамические библиотеки ссылок, связанные с определенными операционными системами). Это сказало ... есть вариация большинства языков программирования, которые могут быть описаны как скомпилированные или интерпретированные.

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

Стивен Робинсон
источник
1

Все языки программирования требуют перевода человеческих понятий в целевой машинный код. Даже язык ассемблера должен быть переведен в машинный код. Этот перевод обычно происходит на следующих этапах:

Этап 1: анализ и перевод (анализ) в промежуточный код. Этап 2: Перевод промежуточного кода в целевой машинный код с заполнителями для внешних ссылок. Этап 3: преобразование внешних ссылок и упаковка в исполняемую программу.

Этот перевод часто называют предварительной компиляцией и «как раз вовремя» (JIT) или компиляцией во время выполнения.

Такие языки, как C, C ++, COBOL, Fortran, Pascal (не все) и Assembly, являются предварительно скомпилированными языками, которые могут выполняться непосредственно операционной системой без необходимости переводчика.

Такие языки, как Java, BASIC, C # и Python интерпретируются. Все они используют этот промежуточный код, созданный на этапе 1, но иногда могут отличаться в том, как они переводят его в машинный код. Самые простые формы используют этот промежуточный код для выполнения подпрограмм машинного кода, которые выполняют ожидаемую работу. Другие скомпилируют промежуточный код до машинного кода и выполнят исправление внешней зависимости во время выполнения. После компиляции он может быть немедленно выполнен. Кроме того, машинный код хранится в кеше предварительно скомпилированного машинного кода многократного использования, который впоследствии может быть повторно использован, если функция потребуется позже. Если функция уже была кэширована, интерпретатору не нужно снова ее компилировать.

Большинство современных языков высокого уровня попадают в интерпретируемую (с JIT) категорию. Это в основном старые языки, такие как C & C ++, которые предварительно скомпилированы.

Марк Бейкер
источник