66. Модули в Python
Оглавление урока
Введение
В предыдущем уроке мы познакомились с генераторными функциями и выражениями в языке Python. Также узнали, как отложить выполнение функции при помощи оператора yield
. С этого урока преступаем к детальному изучению модулей.
Модуль Python – отдельный файл с определенной функциональностью и изолированным пространством имен, предназначенный для многократного применения. Для работы с модулями есть два оператора: import
и from
, с которыми вы уже встречались и функция imp.reload
. Первый оператор позволяет импортеру получить модуль как единое целое. Второй оператор (from
) позволяет извлекать отдельные части из модуля. Функция imp.reload
позволяет перезагрузить код модуля, не останавливая Python.
Давайте по порядку и начнем с архитектуры программы – способом разбития кода на несколько файлов исходного кода (модулей) и связывание их в единое целое. Как работает этот разрозненный код? Есть файл верхнего уровня, содержащий главный поток управление, именно тот, который мы запускаем. Файлы модулей содержат инструменты, которыми пользуется файл верхнего уровня. В свою очередь, модули могут включать в себя другие модули. Несмотря на это, если просто запустить файл модуля, то чаще всего ничего не произойдет. Так и строится архитектура.
Файл верхнего уровня импортирует модуль для использования его инструментов, называемых атрибутами. Атрибуты – имена функций, если по-простому. Рассмотрим это на примере. У нас есть файл a.py
, который будет файлом верхнего уровня.
import b
import c
print('Привет из файла a.py')
Файл b.py
будет выглядеть следующим образом:
def hello():
print('Привет из файла b.py')
Файл c.py
будет вот такой:
def hello():
print('Привет из файла c.py')
Как вы заметили, в двух файлах определена функция hello()
. Благодаря изолированности пространства имен, мы можем пользоваться обеими функциями в файле верхнего уровня:
b.hello()
c.hello()
Оператор import
выполняется по запросу, то есть межфайловое связывание происходит во время выполнения кода. После создается объект с именем модуля, например, b
для модуля b.py
, который мы можем использовать для обращения к его атрибутам.
С операцией «точка» вы уже давно знакомы и знаете о большинстве объектов, хранящих полезные атрибуты, которые извлекаются при помощи операции точки «.
».
Стандартные модули в Python
В Python имеются стандартные библиотечные модули, не являющиеся частью самого языка, которые вы по желанию можете подключать к вашему коду и использовать. Нет необходимости разбираться с ними в контексте курса, так как всегда можно воспользоваться официальной документацией при необходимости. Лучше разберемся в том, что происходит при импортировании.
Как работает импортирование в Python
Если вы программировали на Си и думаете, что уже знаете, как происходит импортирование в Python, то не спешите пропускать этот пункт, есть нюансы.
При импортировании происходит не просто вставка одного текста в другой. Выполняются три шага при первичном импортировании заданного файла:
- Находится файл модуля;
- При необходимости компилируется в байт код;
- Выполняется код модуля создания определенных в нем объектов.
Три шага выполняются только при первичном импортировании модуля. Далее модуль извлекается из памяти. Python хранит модули в таблице с именем sys.modules
.
Для начала следует найти файл модуля, на который мы ссылаемся в import
. Вероятно, вы обратили внимание что в импортировании не указывались расширение и путь до файла. Python использует стандартный путь поиска модулей, к которому мы еще вернемся.
Далее идет (возможная) компиляция в байт-код. Во время этой операции проверяется время модификации файлов байт-кода и исходного кода, а также номер версии байт-кода для принятия решения. Итак, компилировать или нет?
Компиляция происходит, если файл байт-кода старее файла исходного кода или создан в другой версии Python. Для избегания конфликтов в версиях Python 3.2. и выше, файлы байт-кода вынесены в отдельный каталог __pycache__
. Это сделана на случай, когда установлено несколько версий Python. В противоположном случае файл не компилируется. Кстати, расширение байт-кода .pyc
. В случае, если Python по каким-либо причинам не может создать файл с байт-кодом, программа все равно успешно выполнится, так как байт-код в таком случае сохранится в памяти и после завершения программы сотрется.
На последнем шаге выполняется байт-код: все операции модуля выполняются по очереди.
Как вы поняли, любой модуль импортируется один раз за процесс. Следовательно, чтобы динамически в нем что-то поменять, необходимо его загрузить повторно при помощи функции imp.reload
, о которой будет далее.
Вот и возвращаемся к поиску модулей. Поиск состоит из следующих компонентов, т.е. где он производится:
- Домашний каталог программы;
- Каталоги
PYTHONPATH
(при наличии); - Каталоги стандартной библиотеки;
- Содержимое файлов
.pth
; - Подкатолог
site-packages
со сторонними расширениями.
Пять этих компонентов образуют sys.path
со списком имен каталогов. Домашний каталог программы и каталоги стандартной библиотеки определяются автоматически, а каталоги PYTHONPATH
и содержимое файлов .pth
можно использовать для добавления своих каталогов исходного кода.
После поиска в домашнем каталоге программы, выполняется поиск в перечисленных каталогах переменной среды PYTHONPATH
. Кстати, эта переменная среды не устанавливается автоматически. Далее поиск переходит в каталоги стандартной библиотеки, установленные по умолчанию. После чего поиск осуществляется по каталогам, перечисленным в файлах с расширением .pth
. Это аналог упомянутой выше переменной среды, только каталоги перечисляются в текстовом файле с расширением .pth
, каждый на своей строке. На самом деле не все так просто, поэтому в первое время используйте переменную среды PYTHONPATH
. В самом конце поиск осуществляется в подкаталоге site-packages
, установленном по умолчанию.
На самом деле выше описанный способ поиска файлов является обобщенным и может отличаться в разных версиях, платформах, реализациях и так далее.
Чтобы узнать фактические пути поиска известные Python, просто можете вывести распечатать этот список на экране:
import sys
print(sys.path)
В операторе import
мы неслучайно не указываем расширение, так оно может быть не только .py
. Например:
.pyc
– файл байт кода;.pyo
– файл оптимизированного байт-кода;.dll
– модуль расширения.
И многие другие расширения, о которых вы в процессе обучения узнаете. Многие модули, с которыми вы встретитесь, написаны на языке Си и выглядят в точности так, как модули на Python, о чем вам не стоит беспокоиться.
Не забывайте этапы поиска. Если несколько модулей с одинаковыми именами разбросаны по разным каталогам, то при поиске подвяжется найденный первым. А вот в случае с файлами, размещенными в одном каталоге, с одинаковыми именами, но разными расширениями, выбор не будет с течением времени гарантированно одинаковым. Чтобы не сталкиваться с такой ситуацией – задавайте разные имена файлам.
Кстати, что касается файлов оптимизированного байт-кода, они не так часто используются, так как дают прирост в скорости около 5%.
Подытожим, модули в языке Python используются для разграничения пространства имен и многократного использования кода. В этом уроке более детально познакомились с оператором import
и его поведением. Так же узнали о конфигурировании путей поиска.