Скрипты и модули

Создадим два файла.

Файл mymodule.py.

print("Начало импортирования модуля mymodule.py")

def f():
    print("Вызов функции f из модуля mymodule.py" )

print("Конец импортирования модуля mymodule.py")

Файл myscript.py

import mymodule 

print(type(mymodule))

mymodule.f()

Если эти файлы лежат в одной папке, то запуск myscript.py приведет к следующему результату.

>>> python myscript.py
Начало импортирования модуля mymodule.py
Конец импортирования модуля mymodule.py
<class 'module'>
Вызов функции f из модуля mymodule.py
>>> 

Разберем, что здесь происходит.

  • import mymodule импортирует функцию f из модуля с именем mymodule. Т.к. такого модуля нет в стандартной библиотеке и среди установленных внешних библиотек, то python ищет модуль в текущей папке, находит файл mymodule.py и импортирует его. При импортировании модуля, его файл с исходным кодом интерпретируется. Чтобы сократить время повторных импортирований byte code сохраняется в папке __pycache__, что позволяет пропустить этап компиляции в байт код, если уже существует актуальная версия в кэше.

  • По завершении импортирование управление возвращается импортирующему модулю (скрипт myscript.py в данном случае). Результатом импортирования является объект типа module, который связывается с именем mymodule в пространстве имен myscript.py. Если бы строка импортирования выглядела from mymodule import f, то в пространство имен myscript.py заводится имя f, которое связывается с объектом по имени f из пространства имен mymodule.

  • Выражение mymodule.f говорит интерпретатору искать имя f в пространстве имен модуля mymodule;

Пространства имен модулей

Более подробно пространства имён буду обсуждаться позже, а здесь обратим внимания на пространства имён модулей. Пространство имён — таблица имен и связанных с ними объектов. Таких таблицы в любой момент в программе несколько, например, таблица globals “глобальных” переменных и таблица locals локальных переменных, например, для функции, а также пространство builtins встроенных функций, которое доступно абсолютно везде и в любой момент времени интерпретации.

У каждого модуля своё пространство имён globals, т.е. глобальность пространства имён распространяется только на текущий модуль (файл). Это значит, что имена созданные в импортированном модуле напрямую не доступны в импортирующем модуле, если не были явно импортированы (from mymodule import *). Встроенная функция globals возвращает словарь переменных (ключ — имя, значение — объект) из глобального пространства имён. Чтобы продемонстрировать это изменим содержимое предыдущих файлов следующим образом.

Файл mymodule.py.

print("Начало импортирования модуля mymodule.py")

x = 0

print(globals().keys())

print("Конец импортирования модуля mymodule.py")

Файл myscript.py

import mymodule 

y = 1

print(globals().keys())

Тогда запуск файла myscript.py привет к чему-то похожему

>>> python myscript.py
Начало импортирования модуля mymodule.py 
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'x'])
Конец импортирования модуля mymodule.py 
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'mymodule', 'y'])

>>> 

Можно заметить, что глобальные пространства имён не только имёют уникальные друг для друга переменные x и y, но имеются и некоторые другие отличия, связанные с тем, что myscript.py запущен непосредственно, а mymodyle.py загружается при импортировании.

Модуль и скрипт

Чтобы выяснить, запущен ли данный файл непосредственно или импортирован в другом файле используется выражение __name__ == "__main__". Такой подход основывается на том, что python автоматически создаёт имя “__name__” в глобально пространстве имен каждого модуля и скрипта в момент начала их обработки, и связывает это имя с строкой “__main__”, если файл запущен в качестве скрипта и с именем файла (за исключением расширения .py), если файл запускается при импортировании.

Продемонстрируем это на следующих файлах

Файл mymodule.py.

print("Начало импортирования модуля mymodule.py")

print(__name__)

if __name__ == "__main__":
    print("Я был непосредственно запущен")

print("Конец импортирования модуля mymodule.py")

Файл myscript.py

import mymodule 

print(__name__)

if __name__ == "__main__":
    print("Я был непосредственно запущен")

Тогда запуск файла myscript.py привет к чему-то похожему

>>> python myscript.py
Начало импортирования модуля mymodule.py
mymodule
Конец импортирования модуля mymodule.py
__main__                                                                                                                                                 Я был непосредственно запущен

>>>