Как интерпретатор python знает, когда компилировать и обновлять файл .pyc?

Я знал, что файл .pyc генерируется интерпретатором python и содержит байтовый код, как это сказал question.

Я думал, что интерпретатор python использует метку времени, чтобы определить, является ли .pyc более новым, чем .py, и если это так, пропустил его компиляцию при выполнении. (Способ создания make файла)

Итак, я сделал тест, но, похоже, я ошибся.

  • Я написал t.py содержит print '123' и t1.py содержит import t. Запуск команды python t1.py дал результат 123 и сгенерированный t.pyc, все как ожидалось.
  • Затем я отредактировал t.py как print '1234' и обновил отметку времени t.pyc с помощью touch t.pyc.
  • Запустите python t1.py снова, я думал, что получаю 123, но 1234 в самом деле. Таким образом, казалось, что интерпретатор python все еще знал, что t.py обновляется.

Затем я задавался вопросом, будет ли интерпретатор python компилироваться и генерировать t.pyc каждый раз при запуске python t1.py. Но когда я запускал python t1.py несколько раз, я обнаружил, что t.pyc не будет обновляться, если t.py не обновляется.

Итак, мой вопрос: как интерпретатор python знает, когда компилировать и обновлять файл .pyc?

Обновление

Так как интерпретатор python использует временную метку, хранящуюся в файле .pyc. Я думаю, что это запись о том, когда .pyc был в последний раз обновлен. И при импорте сравните его с меткой времени файла .py.

Итак, я попытался взломать его таким образом: измените время работы ОС на более старый и отредактируйте файл .py. Я думал, что при импорте снова .py кажется старше, чем .pyc, и интерпретатор python не будет обновлять .pyc. Но я снова ошибся.

Итак, интерпретатор python сравнивает эти две временные метки не по старому или новному пути, а точно так же?

В прямолинейном эквиваленте я означает, что временная метка в .pyc записывает, когда последний раз был изменен .py. При импорте он сравнивает временную метку с текущей меткой времени .py, если она не то же самое, перекомпилируйте и обновите .pyc.

Ответ 1

Похоже, что метка времени хранится непосредственно в файле *.pyc. Интерпретатор python не полагается на последний атрибут модификации файла, возможно, чтобы избежать проблем с несовместимыми байт-кодами при копировании исходных деревьев.

Рассматривая реализацию python инструкции import, вы можете найти устаревшую проверку в _validate_bytecode_header(). По внешнему виду он извлекает байты с 4 по 7 (вкл.) И сравнивает их с временным кодом исходного файла. Если они не совпадают, байт-код считается заторможенным и, таким образом, перекомпилирован.

В этом процессе он также проверяет длину исходного файла на длину источника, используемого для генерации заданного байт-кода (сохраняется в байтах с 8 по 11).

В реализации python, если одна из этих проверок терпит неудачу, загрузчик байт-кода вызывает ImportError, улавливаемый SourceLoader.get_code(), который запускает перекомпиляция байт-кода.

Примечание.. Как это делается в версии python importlib. Я предполагаю, что в родной версии нет функциональной разницы, но мой C слишком ржавый, чтобы выкопать код компилятора

Ответ 2

Как вы думаете, он эффективно основан на отметке времени последнего обновления .py. Если .py обновлено после генерации .pyc, байт-код будет регенерировать. Это то же поведение, что и make (перекомпилируйте только свежие файлы).

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