НОЧУ ДПО "Национальный открытый университет "ИНТУИТ"
Опубликован: 24.01.2021 | Доступ: свободный | Студентов: 2489 / 106 | Длительность: 03:57:00
Лекция 20:

Импорт модуля. Создание дистрибутива модуля

< Лекция 1 || Лекция 20: 12

Смотреть на youtube

Проект для лекции Lecture7.rar.

Я уже неоднократно повторял, что повторное использование кода лежит в основе современного программирования, - без этого невозможно создавать сложные проекты в приемлемое время их разработки. Работая на языке C# в среде Visual Studio, я привык начинать разработку любого проекта с создания DLL - библиотеки классов, легко подключаемой к любому проекту, в первую очередь к интерфейсным проектам, связанным с решением стоящей передо мной конкретной задачей. Зачастую, к созданному проекту присоединялись ранее созданные DLL - инструментарий, созданный за годы работы. Никаких проблем не возникало, в каких бы каталогах не находились различные DLL. Достаточно было указать ссылку на соответствующий каталог и подключалась библиотека, содержащая повторно используемый код.

Работая с Python , я был совершенно уверен, что и здесь проблема повторного использования кода решается достаточно просто. В PPython ython роль DLL играют пакеты. Стандартная библиотека создана и пополняется избранными членами сообщества Python программистов. Рядовые члены сообщества могут создавать пакеты, добавляемые в библиотеку сторонних модулей. Специальный сайт PyPI, создан для поддержки этого процесса. Однако добавление пакета в эту библиотеку не простая задача, решаемая в несколько строчек кода простым указанием ссылки. В этой лекции рассмотрим, как же решается эта задача.

Давайте вернемся к рассматриваемой на прошлой лекции задаче установления связи между модулями. Уточним постановку задачу. Предположим, что строится новый проект, содержащий два модуля. Один модуль - services.py отвечает за бизнес-логику задачи. Другой модуль - Lecture7.py отвечает за интерфейс задачи. Он является главным модулем проекта. Оба модуля находятся в одном каталоге - Lecture7. В проекте необходимо использовать ранее созданный модуль myservices.py, который находится в другом каталоге - Lecture6.

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

import services, myservices

Если с импортом первого модуля проблем не возникает, то второй модуль будет подчеркнут, что свидетельствует об ошибке импорта с появлением всплывающего сообщения: unresolved import 'myservices'. Это означает, что интерпретатор Python не нашел модуль в тех каталогах, в которых он ищет имена модулей.

В каких каталогах разыскивается имя модуля? Выяснить это позволяет переменная path стандартного модуля sys, которая показывает все каталоги, в которых разыскивается имя модуля, указанное в предложении импорта.

Закомментируем предложение импорта, приводящее к ошибке на этапе выполнения, и импортируем модули стандартной библиотеки, которые могут нам понадобиться:

import os, sys

Выведем на печать содержимое переменной path:

print('sys.path:')
print(sys.path)
print()

Значением этой переменной является список строк следующего содержания:


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

Заметьте, в список входит строка, задающая текущий каталог. Это означает, что переменная sys.path формируется до начала выполнения кода главного модуля на этапе его создания. Когда в выполняемом коде встречается предложение импорта, то для поиска используется созданная переменная.

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

Вариант первый. Модификация переменной path

Кажется вполне естественным добавить в переменную path строку, задающую каталог, где находится импортируемый модуль, а предложение импорта поставить после модификации переменной. Такой вариант решения я рассматривал в предыдущей лекции. Вставить строку в список можно в начало списка, используя метод insert, или в конец списка, используя метод append. Вот код, выполняющий эти действия:

mypath = 'd:\Python\PythonBook\BookProject\Lecture6\Lecture6'
sys.path.insert(0, mypath)
print('sys.path:')
print(sys.path)
print()
import services, myservices

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


Однако прием этот не работает, - по-прежнему импортируемый модуль не находится. Чем это можно объяснить? Предложения импорта, где бы они не находились на глобальном уровне, выполняются первыми, до выполнения кода, модифицирующего переменную path.

Так неужели такой привлекательный вариант с модификацией переменой path не работает. Оказывается работает! Существует элегантное решение проблемы. Как было сказано, предложения импорта, в каком бы месте выполняемого кода они не стояли, выполняются первыми, когда выполняемый код начинает работать. По этой причине программисты Python предпочитают записывать предложения импорта в начале выполняемого кода.

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

def import_method():
    import services, myservices
    return services, myservices

services, myservices = import_method()

Заметьте, метод import_method скрывает предложения импорта, возвращая соответствующие объекты в качестве результата. Метод вызывается уже после модификации переменной path, что было выполнено в предыдущем фрагменте кода. Теперь имена обоих модулей доступны на глобальном уровне и можно вызывать соответствующие сервисы, предоставляемые модулями. Вот пример вызова сервисов:

y = services.ff(5)
print('y = ', y)
y1 = myservices.TranslateFromPtoQ('14', 10, 2)
print('y1 = ', y1)

Все корректно работает, и вот результаты:


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

< Лекция 1 || Лекция 20: 12
Алексей Авилов
Алексей Авилов

Неужели не нашлось русских специалистов, чтобы записать курс по пайтону ? Да, можно включить переводчик и слушать с переводом, но это что? Это кто-то считает хорошим и понятным курсом для начинающих? 

Елена Лаптева
Елена Лаптева

Думаю. что не смогу его закончить. Хотелось предупредить других - не тратьте зря время, ищите другой курс.