Проект для лекции 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 строку, задающую каталог, где находится импортируемый модуль, а предложение импорта поставить после модификации переменной. Такой вариант решения я рассматривал в предыдущей лекции. Вставить строку в список можно в начало списка, используя метод 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.
В интернете и, например, в популярном учебнике Марка Лутца "Изучаем Python" можно найти утверждение, что решить проблему поиска имени импортируемого модуля можно, модифицируя значение переменной окружения PYTHONPATH. Давайте проверим этот вариант решения проблемы. Как добраться до переменной окружения? Это нетрудно. В модуле os из стандартной библиотеки есть атрибут environ (environment - окружение). Это словарь, ключом пары в словаре является строка, задающая имя переменной окружения. Второй элемент пары - это строка, задающая значение переменной окружения. Так что вызов os.environ["PYTHONPATH"] позволяет получить значение этой переменной. При создании модуля эта переменная хранит путь к каталогу модуля. Полагаю, что переменная sys.path использует переменную окружения для включения текущего каталога в свой список. Поэтому, модифицировав переменную окружения, можно надеяться, что и переменная sys.path будет
модифицирована.
Так что вместо того, чтобы модифицировать непосредственно sys.path попробуем модифицировать переменную окружения PYTHONPATH. Вот соответствующий код:
print('PYTHONPATH = ', os.environ['PYTHONPATH'])
mypath = 'd:\\Python\\PythonBook\\BookProject\\Lecture6\\Lecture6'
os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + '; ' + mypath
print('PYTHONPATH = ', os.environ['PYTHONPATH']
print('sys.path = ', sys.path)
Приведу результаты выполнения этого фрагмента кода:
Как видите, переменная окружения содержит текущий каталог. Ее можно модифицировать, добавив соответствующий каталог. Однако, эта модификация никак не отражается на переменной sys.path. Понятно почему. Связь между этими переменными устанавливается раньше, чем выполняется модификация переменной окружения. Повлиять на этот порядок действий не представляется возможным. Полагаю, что вариант с модификацией переменной окружения не дает решения нашей задачи.
Если сервисный модуль, который мы разработали, может быть полезен для многих проектов, то его целесообразно поместить в библиотеку сторонних модулей. В этом случае не будет необходимости подключать его для каждого проекта в отдельности. Применяемая техника подключения рассчитана на пакеты модулей и, по сути, позволяет поделиться разработанным пакетом с другими Python программистами. Для решения задачи необходимо вначале создать дистрибутив пакета - комплект, позволяющий распространять пакет, Дистрибутив пакета можно разместить в хранилище сторонних модулей. Стандартная библиотека имеет средства - модуль setuptools, поддерживающие создание дистрибутива.
Работа формирования дистрибутивного комплекта начинается с создания его описания. Для этого нужно в каталоге, где находится сервисный модуль создать два файла:
setup.py - импортирует и запускает метод setup из стандартного модуля setuptools,readme.txt - содержит текст, описывающий сервисы модуля.Вот текст модуля (файла) setup.py, который я подготовил для включения в хранилище сервисного модуля myservices:
from setuptools import setup
setup(
name = 'myservices',
description = 'different services',
author = 'Vladimir',
author_email = 'vladimir@gmail.com',
url = '',
py_modules = ['myservices'])
Заметьте, метод setup имеет много параметров, указывать их все не обязательно, можно использовать значения по умолчанию.
Текстовый файл readme может содержать произвольный текст. Я включил в него следующий текст:
Этот модуль содержит 4 полезных сервиса: - вычисление определенного интеграла, - перевод чисел из одной системы счисления в другую, - вычисление площади выпуклого многоугольника, - быструю сортировку массивов.
Создание файла дистрибутива можно выполнить в командной строке, которую следует открыть в каталоге, содержащем три файла: myservices.py, setup.py, readme.txt. В командной строке запускаем программу setup.py, передав ей в качестве аргумента sdist.
Вот как выглядит консольное окно в результате выполнения этой команды:
Приводимые сообщения показывают, что работа по созданию дистрибутива успешно выполнена. Появилось лишь одно предупреждение, что пропущены требуемые метаданные по указанию url. Данное предупреждение не влияет на корректность работы.
Видимым результатом работы является создание папки dist, в которой содержится созданный архивированный файл дистрибутива - myservices-0.0.0.tar.gz. Этим файлом вы можете делиться со всеми, кому интересен созданный пакет (модуль).
Осталось поместить дистрибутив в хранилище сторонних пакетов. Для этого используется специальный инструмент Python , называемый pip (Package Installer for Python)
Запускать его можно в командной строке. Но прежде чем использовать его для установки дистрибутива, я использовал его для обновления последней версии этого инструмента, выполнив команду:
py -3 -m pip install -upgrade pip
Вот как выглядит консольное окно в результате выполнения этой команды:
После чего я использовал pip для установки дистрибутива, выполнив команду:
py -3 - m pip install myservices-0.0.0.tar.gz
Вот как выглядит консольное окно в результате выполнения этой команды:
Теперь сервисный модуль myservices находится в хранилище сторонних модулей и может импортироваться в любом проекте без всяких дополнительных настроек, также как импортируются модули стандартной библиотеки.
Подведем итоги. Если необходимо импортировать ранее созданный модуль в новом проекте, то есть три возможности:
Каждый из этих способов имеет свои преимущества и свою сферу применения.
Проект для лекции 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 строку, задающую каталог, где находится импортируемый модуль, а предложение импорта поставить после модификации переменной. Такой вариант решения я рассматривал в предыдущей лекции. Вставить строку в список можно в начало списка, используя метод 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.
В интернете и, например, в популярном учебнике Марка Лутца "Изучаем Python" можно найти утверждение, что решить проблему поиска имени импортируемого модуля можно, модифицируя значение переменной окружения PYTHONPATH. Давайте проверим этот вариант решения проблемы. Как добраться до переменной окружения? Это нетрудно. В модуле os из стандартной библиотеки есть атрибут environ (environment - окружение). Это словарь, ключом пары в словаре является строка, задающая имя переменной окружения. Второй элемент пары - это строка, задающая значение переменной окружения. Так что вызов os.environ["PYTHONPATH"] позволяет получить значение этой переменной. При создании модуля эта переменная хранит путь к каталогу модуля. Полагаю, что переменная sys.path использует переменную окружения для включения текущего каталога в свой список. Поэтому, модифицировав переменную окружения, можно надеяться, что и переменная sys.path будет
модифицирована.
Так что вместо того, чтобы модифицировать непосредственно sys.path попробуем модифицировать переменную окружения PYTHONPATH. Вот соответствующий код:
print('PYTHONPATH = ', os.environ['PYTHONPATH'])
mypath = 'd:\\Python\\PythonBook\\BookProject\\Lecture6\\Lecture6'
os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + '; ' + mypath
print('PYTHONPATH = ', os.environ['PYTHONPATH']
print('sys.path = ', sys.path)
Приведу результаты выполнения этого фрагмента кода:
Как видите, переменная окружения содержит текущий каталог. Ее можно модифицировать, добавив соответствующий каталог. Однако, эта модификация никак не отражается на переменной sys.path. Понятно почему. Связь между этими переменными устанавливается раньше, чем выполняется модификация переменной окружения. Повлиять на этот порядок действий не представляется возможным. Полагаю, что вариант с модификацией переменной окружения не дает решения нашей задачи.
Если сервисный модуль, который мы разработали, может быть полезен для многих проектов, то его целесообразно поместить в библиотеку сторонних модулей. В этом случае не будет необходимости подключать его для каждого проекта в отдельности. Применяемая техника подключения рассчитана на пакеты модулей и, по сути, позволяет поделиться разработанным пакетом с другими Python программистами. Для решения задачи необходимо вначале создать дистрибутив пакета - комплект, позволяющий распространять пакет, Дистрибутив пакета можно разместить в хранилище сторонних модулей. Стандартная библиотека имеет средства - модуль setuptools, поддерживающие создание дистрибутива.
Работа формирования дистрибутивного комплекта начинается с создания его описания. Для этого нужно в каталоге, где находится сервисный модуль создать два файла:
setup.py - импортирует и запускает метод setup из стандартного модуля setuptools,readme.txt - содержит текст, описывающий сервисы модуля.Вот текст модуля (файла) setup.py, который я подготовил для включения в хранилище сервисного модуля myservices:
from setuptools import setup
setup(
name = 'myservices',
description = 'different services',
author = 'Vladimir',
author_email = 'vladimir@gmail.com',
url = '',
py_modules = ['myservices'])
Заметьте, метод setup имеет много параметров, указывать их все не обязательно, можно использовать значения по умолчанию.
Текстовый файл readme может содержать произвольный текст. Я включил в него следующий текст:
Этот модуль содержит 4 полезных сервиса: - вычисление определенного интеграла, - перевод чисел из одной системы счисления в другую, - вычисление площади выпуклого многоугольника, - быструю сортировку массивов.
Создание файла дистрибутива можно выполнить в командной строке, которую следует открыть в каталоге, содержащем три файла: myservices.py, setup.py, readme.txt. В командной строке запускаем программу setup.py, передав ей в качестве аргумента sdist.
Вот как выглядит консольное окно в результате выполнения этой команды:
Приводимые сообщения показывают, что работа по созданию дистрибутива успешно выполнена. Появилось лишь одно предупреждение, что пропущены требуемые метаданные по указанию url. Данное предупреждение не влияет на корректность работы.
Видимым результатом работы является создание папки dist, в которой содержится созданный архивированный файл дистрибутива - myservices-0.0.0.tar.gz. Этим файлом вы можете делиться со всеми, кому интересен созданный пакет (модуль).
Осталось поместить дистрибутив в хранилище сторонних пакетов. Для этого используется специальный инструмент Python , называемый pip (Package Installer for Python)
Запускать его можно в командной строке. Но прежде чем использовать его для установки дистрибутива, я использовал его для обновления последней версии этого инструмента, выполнив команду:
py -3 -m pip install -upgrade pip
Вот как выглядит консольное окно в результате выполнения этой команды:
После чего я использовал pip для установки дистрибутива, выполнив команду:
py -3 - m pip install myservices-0.0.0.tar.gz
Вот как выглядит консольное окно в результате выполнения этой команды:
Теперь сервисный модуль myservices находится в хранилище сторонних модулей и может импортироваться в любом проекте без всяких дополнительных настроек, также как импортируются модули стандартной библиотеки.
Подведем итоги. Если необходимо импортировать ранее созданный модуль в новом проекте, то есть три возможности:
Каждый из этих способов имеет свои преимущества и свою сферу применения.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.