Разница между файлами .py и .pyc: руководство для начинающих по Python

Вам интересно, в чем разница между файлами Python.py и.pyc? Вы попали по адресу.

Файлы с расширением .py содержат код Python, который может быть прочитан человеком. С другой стороны, файлы .pyc содержат байт-код, который не может быть прочитан человеком. Файлы с расширением .py компилируются в файлы .pyc, которые затем обрабатываются интерпретатором Python.

Не волнуйтесь, если это покажется вам непонятным, мы рассмотрим несколько примеров, которые все прояснят.

И я также покажу вам, когда происходит компиляция файлов .py в файлы .pyc.

Давайте приступим!

Что такое файлы .py и .pyc в Python?

Файлы с расширением .py — это исходные файлы Python, в которых вы пишете свой код Python.

Код Python, который вы пишете в файлах .py, не выполняется в том же формате на машине, на которой вы запускаете свой код.

Перед выполнением код в файлах .py компилируется в файлы .pyc.

Представьте себе процесс компиляции как перевод с одного языка на другой язык.

Файлы с расширением .pyc являются результатом компиляции файлов с расширением .py. Файл .pyc для данного модуля Python автоматически создается при импорте этого модуля.

Примечание: как разработчик Python вы будете вносить изменения только в файлы .py.

Чтобы увидеть разницу между двумя типами файлов, давайте сначала создадим модуль Python в файле с именем app.py.

Для всех примеров в этом руководстве я создаю app.py внутри каталога /var/tmp/.

Файл app.py содержит код для модуля app, и в этом примере он содержит одну функцию:

def get_full_name(first_name, last_name):
    return "{} {}".format(first_name, last_name)

Чтобы показать вам формат файла.pyc, мы сначала будем использовать Python 2.

В следующем разделе вы поймете почему…

Откройте оболочку Python и импортируйте модуль приложения:

$ python2

Python 2.7.16 (default, Dec 21 2020, 23:00:36) 
[GCC Apple LLVM 12.0.0 (clang-1200.0.30.4) [+internal-os, ptrauth-isa=sign+stri on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
>>> 

Теперь выйдите из оболочки Python.

Обратите внимание, что файл app.pyc был создан:

$ ls -al app*
-rw-r--r--  1 codefather  wheel   91 Mar 20 00:11 app.py
-rw-r--r--  1 codefather  wheel  261 Mar 20 00:12 app.pyc

Давайте посмотрим на содержимое файла.pyc…

$ cat app.pyc
?
d?ZdS(cCsdj||?S(Ns{} {}(tformat(t
get_full_namesN(R(((sapp.py<module>t%

Файл .pyc не полностью читаем, поскольку это скомпилированная версия исходного файла .py. Файл app.pyc содержит байт-код.

Что такое байт-код?!?

Подумайте о байт-коде как о низкоуровневом представлении кода Python в вашем файле .py. Низкий уровень означает, что он ближе к языку, который может понять компьютер, по сравнению с исходным кодом Python.

Как создается скомпилированный файл в Python?

Мы видели, что при импорте модуля Python создается скомпилированный файл (.pyc).

Но что создает скомпилированные файлы Python?

Ответ: это зависит от используемой вами реализации Python.

Эталонная реализация Python называется CPython и написана на C и Python. В этой реализации код Python компилируется в байт-код компилятором перед интерпретацией.

Как можно убедиться, что вы используете CPython?

Для проверки реализации Python, которую вы используете на своей машине, вы можете использовать модуль платформы Python. А конкретно функцию python_implementation().

Давайте посмотрим, какую реализацию Python 2 я использую на этой машине.

Python 2.7.16 (default, Dec 21 2020, 23:00:36) 
[GCC Apple LLVM 12.0.0 (clang-1200.0.30.4) [+internal-os, ptrauth-isa=sign+stri on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.python_implementation()
'CPython'

Реализацией Python на этой машине является CPython, который, как я уже объяснял ранее, является эталонной реализацией Python.

Давайте посмотрим, что получится в результате для Python 3.

Python 3.8.2 (default, Dec 21 2020, 15:06:03) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.python_implementation()
'CPython'

Та же реализация: CPython.

Где создаются скомпилированные файлы при использовании Python 3?

В предыдущем разделе мы использовали Python 2. Мы видели, что при импорте модуля в том же каталоге, что и файл .py, был создан файл .pyc.

Примечание: Учитывая, что Python 2 очень старый, вам на самом деле следует использовать Python 3. В этом уроке я также использую Python 2, чтобы показать вам разницу в поведении между двумя версиями Python.

Давайте попробуем провести тот же тест с Python 3.

Удалите созданный ранее файл.pyc и откройте оболочку Python с помощью Python 3.

Затем импортируйте модуль приложения

$ rm app.pyc
$ python3
Python 3.8.2 (default, Dec 21 2020, 15:06:03) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
>>>

Теперь выйдите из оболочки Python и проверьте, существует ли новый файл .pyc.

>>> exit()
$ ls -al app*
-rw-r--r--  1 codefather  wheel  91 Mar 20 00:11 app.py

Это странно…

По какой-то причине скомпилированный файл с расширением.pyc не существует.

Почему?!?

Это потому что…

В Python 3 скомпилированная версия кода для данного модуля создается в другом месте по сравнению с тем, что происходит в Python 2.

Давайте снова откроем оболочку Python 3…

…Я хочу вам кое-что показать.

Python 3.8.2 (default, Dec 21 2020, 15:06:03) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
>>> dir(app)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'get_full_name']

После импорта модуля приложения мы можем использовать встроенную функцию dir(), чтобы получить список атрибутов модуля, к которым мы можем получить доступ.

Хочу обратить ваше внимание на один атрибут: __cached__.

Давайте проверим его ценность…

>>> app.__cached__
'/Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc'

Примечание: как уже упоминалось ранее, во всех примерах этого руководства я создал app.py внутри каталога /var/tmp/.

Атрибут __cached__ для модуля Python — это путь к скомпилированной версии этого модуля. Добавление этого атрибута было частью предложения PEP 3147.

Примечание: PEP означает предложения по улучшению Python.

Вы можете видеть, что формат имени файла.pyc изменился по сравнению с Python 2. При использовании Python 3 имя файла также содержит реализацию Python (cpython) и версию Python (38).

Путь к файлу зависит от вашей операционной системы.

Давайте проверим, что файл app.cpython-38.pyc действительно находится в этом каталоге.

$ ls -al /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc
-rw-r--r--  1 codefather  staff  257 Mar 20 00:19 /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc

Мы подтвердили, что скомпилированный файл находится в этом каталоге.

Было бы сложнее найти этот путь без извлечения значения атрибута __cached__!

Когда обновляются файлы.pyc?

Давайте продолжим работу над примером из предыдущего раздела.

Мы хотим понять, когда обновляются файлы .pyc.

Снова откройте оболочку Python 3, импортируйте модуль приложения и проверьте, изменилось ли что-нибудь в файле .pyc:

$ python3
Python 3.8.2 (default, Dec 21 2020, 15:06:03) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
>>> exit()

$ ls -al /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc
-rw-r--r--  1 codefather  staff  257 Mar 20 00:19 /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc

Как видно из вывода команды ls, размер файла и дата последнего изменения app.cpython-38.pyc не изменились.

Теперь измените функцию Python, определенную в app.py, и измените имя функции с get_full_name() на get_user_full_name:

def get_user_full_name(first_name, last_name):
    return "{} {}".format(first_name, last_name)

Откройте оболочку Python 3, импортируйте модуль приложения и выйдите из оболочки.

>>> import app
>>> exit()

Проверьте, изменилось ли что-нибудь в скомпилированном файле app.cpython-38.pyc:

$ ls -al /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc
-rw-r--r--  1 codefather  staff  262 Mar 20 00:31 /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc

Изменились размер файла и дата последнего изменения.

Это произошло потому, что интерпретатор Python обнаружил изменение в модуле app.py и перекомпилировал код в новый файл .pyc.

Python воссоздает файл .pyc для данного модуля, когда этот модуль изменяется и повторно импортируется.

Можно ли удалить файлы.pyc?

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

Здесь вы можете увидеть созданный ранее.pyc.

$ ls -al /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc
-rw-r--r--  1 codefather  staff  262 Mar 20 00:31 /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc

Давайте удалим его и снова импортируем модуль приложения…

$ rm /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc
$ python3

Python 3.8.2 (default, Dec 21 2020, 15:06:03) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
>>> exit()

$ ls -al /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc
-rw-r--r--  1 codefather  staff  262 Mar 20 00:35 /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc

Python пересоздал файл .pyc. Он перекомпилировал файл .py в этот файл .pyc.

Можно ли удалить файлы.py с помощью Python 2?

Это интересно…

Давайте попробуем удалить файл app.py, содержащий наш код Python.

Как вы думаете, что произойдет, если мы попытаемся импортировать модуль приложения?

Начнем с Python 2 и перед удалением файла .py убедитесь, что файл .pyc существует в том же каталоге, что и файл .py.

Если файл .pyc не существует, откройте оболочку Python 2 и импортируйте модуль приложения, чтобы заново создать файл .pyc.

$ python2

Python 2.7.16 (default, Dec 21 2020, 23:00:36) 
[GCC Apple LLVM 12.0.0 (clang-1200.0.30.4) [+internal-os, ptrauth-isa=sign+stri on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
>>> exit()

Теперь удалите app.py и снова откройте оболочку Python 2, чтобы импортировать модуль app.

$ rm app.py
$ python2

Python 2.7.16 (default, Dec 21 2020, 23:00:36) 
[GCC Apple LLVM 12.0.0 (clang-1200.0.30.4) [+internal-os, ptrauth-isa=sign+stri on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
>>> dir(app)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'get_user_full_name']
>>> app.get_user_full_name('John', 'Smith')
'John Smith'

Интересно, что после удаления файла .py интерпретатор Python 2 не выдает никаких ошибок при импорте этого модуля, если файл .pyc для этого модуля существует.

Можно ли удалить файлы.py с помощью Python 3?

Давайте продолжим с того места, на котором остановились в предыдущем разделе, где мы рассмотрели, как ведет себя Python 2 при удалении файла.py.

А теперь давайте воспользуемся Python 3.

Файл app.py еще не существует в текущем каталоге /var/tmp, поэтому мы можем просто открыть оболочку Python 3 и попытаться импортировать модуль app.

$ python3
Python 3.8.2 (default, Dec 21 2020, 15:06:03) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: bad magic number in 'app': b'\x03\xf3\r\n'

Мы получаем странную ошибку: неверное магическое число в «app».

Что это значит?

Одним из сценариев, в котором возникает ошибка неверного магического числа, является ситуация, когда Python 3 пытается загрузить файл .pyc, скомпилированный с помощью Python 2.

По-видимому, это происходит потому, что Python 3 находит файл .pyc в текущем каталоге и пытается загрузить его.

Давайте удалим файл .pyc из текущего каталога, а затем попробуем снова импортировать модуль приложения.

$ pwd
/var/tmp
$ rm app.pyc
$ python3
Python 3.8.2 (default, Dec 21 2020, 15:06:03) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import app
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'app'

Мы получаем ошибку ModuleNotFoundError, несмотря на то, что файл .pyc все еще существует в каталоге, в котором он был создан при импорте модуля приложения с помощью Python 3 (см. вывод ls ниже).

$ ls -al /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc
-rw-r--r--  1 codefather  staff  262 Mar 20 00:35 /Users/codefather/Library/Caches/com.apple.python/private/var/tmp/app.cpython-38.pyc

Заключение

Теперь вы знаете, в чем разница между файлами.py и.pyc в Python.

Вы также знаете, какую роль играют файлы .pyc в выполнении ваших программ Python: они генерируются путем компиляции файлов .py, а затем интерпретируются.

Мы также увидели, как компиляция файла .pyc различается между Python 2 и Python 3.

Автор

Фото аватара

Владимир Михайлов

Программист на Python с большим количеством опыта и разнообразных проектов.