Вступление
Модули - это организационная единица высшего уровня в Python. Если вы хотя бы немного знакомы с Python, вы, вероятно, не только использовали готовые модули, но и создали несколько самостоятельно. Так что же такое модуль? Модули - это единицы, которые хранят код и данные, обеспечивают повторное использование кода для проектов Python, а также полезны при разделении пространств имен системы на автономные пакеты. Они самодостаточны, потому что вы можете получить доступ к атрибутам модуля только после его импорта. Вы также можете понимать их как пакеты имен, которые при импорте становятся атрибутами импортируемого объекта модуля. Фактически, любой файл Python с расширением .py представляет собой модуль.
В этой статье мы начнем с основных основ создания и импорта модулей, чтобы перейти к более сложным сценариям использования модулей, а также к упаковке и отправке ваших модулей в «официальный» репозиторий программного обеспечения Python, разделенных соответственно на три части: Создание модуля , Использование модуля и отправка пакета в PyPI .
Создание модуля
Основы
На самом деле нет особой философии в создании модуля Python, поскольку файлы с суффиксом .py представляют собой модуль. Хотя не каждый файл Python предназначен для импорта в виде модуля. Файлы Python, которые используются для запуска в качестве автономного приложения Python (файлы верхнего уровня), обычно предназначены для запуска как сценарии, и их импорт фактически запускает команды в сценарии.
Модули, которые предназначены для импорта другим кодом, не будут выполнять какой-либо код, а только отображают его имена верхнего уровня в качестве атрибутов для импортируемого объекта. Также возможно разработать модули Python с двухрежимным кодом, которые можно использовать как для импорта, так и для запуска в качестве сценария верхнего уровня.
В то время как правила создания модуля являются довольно расслаблены, есть одно правило на модуле именования. Поскольку имена файлов модулей при импорте становятся именами переменных в Python, не разрешается называть модули зарезервированными словами Python. Например, модуль for.py может быть создан, но не может быть импортирован, потому что «for» - зарезервированное слово. Давайте проиллюстрируем то, что мы уже упоминали, в «Hello world!» пример.
# Module file: my_module.py
def hello_printer():
print("Hello world!")
name = "John"
# Script file: my_script.py
import my_module
my_module.hello_printer()
print("Creator:", my_module.name)
My_module.py разработан как модуль, код которого можно импортировать и
повторно использовать в других файлах Python. Вы можете видеть это по
его содержанию: он не требует никаких действий, а просто определяет
функции и переменные. Напротив, my_script.py разработан как сценарий
верхнего уровня, который запускает программу Python - он явно вызывает
функцию hello_printer
и выводит значение переменной на экран.
Запустим в терминале файл my_script.py:
$ python my_script.py
Hello world!
Creator: John
Как отмечалось ранее, важным выводом из этого первого базового примера является то, что имена файлов модулей важны. После импорта они становятся переменными / объектами в модуле импортера. Все определения кода верхнего уровня в модуле становятся атрибутами этой переменной.
Под «верхним уровнем» я подразумеваю любую функцию или переменную,
которая не вложена в другую функцию или класс. Затем к этим атрибутам
можно получить доступ с помощью стандартного <object>.<attribute>
в
Python.
В следующем разделе мы сначала рассмотрим «общую картину» многофайловых программ Python, а затем рассмотрим файлы Python «двойного режима».
Архитектура программы
Любая нетривиальная программа Python будет организована в несколько файлов, связанных друг с другом с помощью импорта. Python, как и большинство других языков программирования, использует эту модульную структуру программы, в которой функции сгруппированы в многоразовые блоки. В целом в многофайловом приложении Python можно выделить три типа файлов:
- файл верхнего уровня : файл Python или скрипт , который является основной точкой входа в программу. Этот файл запускается для запуска вашего приложения.
- определяемые пользователем модули : файлы Python, которые импортируются в файл верхнего уровня или между собой и предоставляют отдельные функции. Эти файлы обычно не запускаются непосредственно из командной строки и создаются специально для целей проекта.
- стандартные библиотечные модули : предварительно закодированные модули, встроенные в установочный пакет Python, такие как платформенно-независимые инструменты для системных интерфейсов, сценарии Интернета, создание графического интерфейса и другие. Эти модули не являются частью самого исполняемого файла Python, а являются частью стандартной библиотеки Python .
На рисунке 1 показан пример структуры программы с тремя типами файлов:
{.ezlazyload .img-responsive}
Рисунок 1: Пример структуры программы, включая сценарий верхнего уровня, пользовательские модули и стандартные библиотечные модули.
На этом рисунке модуль top_module.py представляет собой файл Python верхнего уровня, который импортирует инструменты, определенные в модуле «module1», но также имеет доступ к инструментам в «module2» через «module1». Два настраиваемых модуля используют ресурсы друг друга, а также другие модули из стандартной библиотеки Python. Цепочка импорта может быть сколь угодно глубокой: количество импортируемых файлов не ограничено, и они могут импортировать друг друга, хотя с циклическим импортом нужно быть осторожным.
Проиллюстрируем это на примере кода:
# top_module.py
import module1
module1.print_parameters()
print(module1.combinations(5, 2))
# module1.py
from module2 import k, print_parameters
from math import factorial
n = 5.0
def combinations(n, k):
return factorial(n) / factorial(k) / factorial(nk)
# module2.py
import module1
k = 2.0
def print_parameters():
print('k = %.fn = %.f' % (k, module1.n))
В приведенном выше примере «top_module.py» - это модуль верхнего уровня,
который будет запускаться пользователем, и он импортирует инструменты из
других модулей через «module1.py». module1
и module2
- это
определяемые пользователем модули, а модуль «math» импортируется из
стандартной библиотеки Python. При запуске скрипта верхнего уровня
получаем:
$ python top_module.py
k = 2 n = 5
10.0
При запуске файла Python верхнего уровня его операторы исходного кода и операторы в импортированных модулях компилируются в промежуточном формате, известном как байт-код , который не зависит от платформы. Файлы байтового кода импортированных модулей хранятся с расширением .pyc в том же каталоге, что и файл .py для версий Python до 3.2, и в каталоге __pycache__ в домашнем каталоге программы в Python 3.2+.
$ ls __pycache__/
module1.cpython-36.pyc module2.cpython-36.pyc
Двухрежимный код
Как упоминалось ранее, файлы Python также могут быть спроектированы как импортируемые модули, так и сценарии верхнего уровня. То есть при запуске модуль Python будет работать как отдельная программа, а при импорте он будет действовать как импортируемый модуль, содержащий определения кода.
Это легко сделать с помощью атрибута __name__ , который автоматически встроен в каждый модуль. Если модуль запускается как сценарий верхнего уровня, атрибут __name__ будет равен строке «__main__», в противном случае, если он импортирован, он будет содержать имя фактического модуля.
Вот пример двухрежимного кода:
# hiprinter.py
# Name definitions part
multiply = 3
def print_hi():
print("Hi!" * multiply)
# Stand-alone script part
if __name__ == '__main__':
print_hi()
Вышеупомянутый файл 'hiprinter.py' определяет функцию, которая будет
доступна клиенту при импорте. Если файл запускается как отдельная
программа, та же функция вызывается автоматически. Разница здесь по
сравнению с примером my_script.py в разделе «Основы»
заключается в том, что при импорте hiprinter.py он не будет запускать
код, вложенный в оператор if __name__ == '__main__'
# Terminal window
$ python hiprinter.py
Hi!Hi!Hi!
# Python interpreter
>> import hiprinter
>> hiprinter.print_hi()
Hi!Hi!Hi!
Двухрежимный код очень распространен на практике и особенно полезен для
модульного тестирования: в то время как переменные и функции определены
как имена верхнего уровня в файле, часть внутри if
может служить
областью тестирования вышеупомянутого определенного имена.
Использование модуля
Заявления об импорте
Пример из раздела « Архитектура программы» был
полезен, чтобы увидеть разницу между двумя операторами import
: import
и from
. Основное отличие состоит в том, что import
загружает весь
модуль как единый объект, а from
загружает определенные свойства и
функции из модуля. Импорт имен с помощью from
затем можно использовать
непосредственно в модуле импортера, не вызывая имя импортированного
объекта.
Использование from
разрешено только на верхнем уровне файла модуля в
Python 3.x, но не внутри функции. Python 2.x позволяет использовать его
в функции, но выдает предупреждение. С точки зрения производительности
from
работает медленнее, чем import
потому что он выполняет всю
работу, которую import
- просматривает все содержимое импортированного
модуля, а затем выполняет дополнительный шаг по выбору подходящих имен
для импорта.
Также существует третий оператор импорта from *
который используется
для импорта всех имен верхнего уровня из импортированного модуля и
использования их непосредственно в классе импортера. Например, мы могли
бы использовать:
from module2 import *
Это позволит импортировать все имена (переменные и функции) из файла module2.py. Этот подход не рекомендуется из-за возможного дублирования имен - импортированные имена могут перезаписать уже существующие имена в модуле импортера.
Путь поиска модуля
Одним из важных аспектов написания модульных приложений Python является поиск модулей, которые необходимо импортировать. Хотя модули стандартной библиотеки Python настроены для глобального доступа, импорт определяемых пользователем модулей через границы каталогов может стать более сложным.
Python использует список каталогов, в которых он ищет модули, известный как путь поиска . Путь поиска состоит из следующих каталогов:
- Домашний каталог программы. Расположение сценария верхнего уровня. Обратите внимание, что домашний каталог может не совпадать с текущим рабочим каталогом .
PYTHONPATH
. Если установлено,PYTHONPATH
определяет конкатенацию определенных пользователем каталогов, в которых интерпретатор Python должен искать модули.- Стандартные библиотечные каталоги. Эти каталоги автоматически устанавливаются при установке Python и всегда ищутся.
- Каталоги, перечисленные в файлах .pth. Этот вариант является
альтернативой
PYTHONPATH
, и он работает, добавляя ваши каталоги, по одному на строку, в текстовый файл с суффиксом .pth , который следует поместить в каталог установки Python, который обычно / usr / local / lib / python3 .6 / на машине Unix или C: \ Python36 \ на машине Windows. - Каталог сайтов-пакетов. В этот каталог автоматически добавляются все сторонние расширения.
PYTHONPATH
, вероятно, является наиболее подходящим способом для
разработчиков включать свои пользовательские модули в путь поиска. Вы
можете легко проверить, установлена ли переменная на вашем компьютере,
что в моем случае приводит к:
$ echo $PYTHONPATH
/Users/Code/Projects/:
Чтобы создать переменную на компьютере с Windows, вы должны использовать инструкции в «Панель управления -> Система -> Дополнительно», в то время как в MacOS и других системах Unix проще всего добавить следующую строку либо в ~ / .bashrc, либо в ~ /. bash_profile , где ваши каталоги объединены знаком двоеточия (":").
export PYTHONPATH=<Directory1:Directory2:...:DirectoryN>:$PYTHONPATH".
Этот метод очень похож на добавление каталогов в ваш Unix $ PATH .
После того, как все каталоги будут найдены в пути поиска во время
запуска программы, они сохранятся в списке, который можно просмотреть с
помощью sys.path
в Python. Конечно, вы также можете добавить каталог в
sys.path
, а затем импортировать свои модули, которые будут изменять
путь поиска только во время выполнения программы.
В любом случае параметры PYTHONPATH
и .pth допускают более
постоянную модификацию пути поиска. Важно знать, что Python сканирует
строку пути поиска слева направо, поэтому модули в самых левых
перечисленных каталогах могут перезаписывать те, которые имеют то же имя
в самой правой части. Обратите внимание, что пути поиска модулей нужны
только для импорта модулей из разных каталогов.
Как показано в следующем примере, пустая строка в начале списка предназначена для текущего каталога:
import sys
sys.path
['',
'/Users/Code/Projects',
'/Users/Code/Projects/Blogs',
'/Users/Code/anaconda3/lib/python36.zip',
'/Users/Code/anaconda3/lib/python3.6',
'/Users/Code/anaconda3/lib/python3.6/site-packages',
'/Users/Code/anaconda3/lib/python3.6/site-packages/IPython/extensions',
'/Users/Code/.ipython']
Подводя итог, можно сказать, что организовать вашу программу Python в виде нескольких взаимосвязанных модулей довольно просто, если ваша программа хорошо структурирована: в виде отдельных, естественно сгруппированных частей кода. В более сложных или плохо структурированных программах импорт может стать обузой, и вам придется заняться более сложными темами импорта.
Модуль перезагружается
Благодаря кешированию модуль можно импортировать только один раз для
каждого процесса. Поскольку Python является интерпретируемым языком, он
запускает код импортированного модуля, как только он достигает
инструкции import
или from
Более поздний импорт в том же процессе
(например, тот же интерпретатор Python) больше не запускает код
импортированного модуля. Он просто получит модуль из кеша.
Вот пример. Давайте повторно используем приведенный выше код в my_module.py, импортируем его в интерпретатор Python, затем изменим файл и повторно импортируем его снова.
>> import my_module
>> print(my_module.name)
John
# Now modify the 'name' variable in 'my_module.py' into name = 'Jack' and reimport the module
>> import my_module
>> print(my_module.name)
John
Чтобы отключить кеширование и разрешить повторный импорт модулей, Python
предоставляет функцию reload
. Давайте попробуем это в том же окне
Python, что и раньше:
>> from imp import reload # Python3.x
>> reload(my_module)
<module 'my_module' from '/Users/Code/Projects/small_example/my_module.py'>
>> print(my_module.name)
Jack
Функция reload
изменяет модуль на месте. То есть, не затрагивая другие
объекты, которые ссылаются на импортированный модуль. Вы можете
заметить, что функция также возвращает сам модуль, указывая его имя и
путь к файлу. Эта функция особенно полезна на этапе разработки, но также
и в более крупных проектах.
Например, для программ, которым требуется постоянное подключение к серверу, гораздо дороже перезапустить все приложение, чем выполнять динамическую перезагрузку или горячую перезагрузку для использования во время разработки.
Модульные пакеты
При импорте имен модулей вы фактически загружаете файлы Python,
хранящиеся где-то в вашей файловой системе. Как упоминалось ранее,
импортированные модули должны находиться в каталоге, который указан в
пути поиска вашего модуля ( sys.path
). В Python есть нечто большее,
чем просто «импорт имен» - вы можете импортировать весь каталог,
содержащий файлы Python, как пакет модуля . Этот импорт известен как
импорт пакетов .
Так как же импортировать пакеты модулей? Давайте создадим каталог с именем «mydir», который включает модуль «mod0.py» и два подкаталога «subdir1» и «subdir2», содержащие модули «mod1.py» и «mod2.py» соответственно. Структура каталогов выглядит так:
$ ls -R mydir/
mod0.py subdir1 subdir2
my_dir//subdir1:
mod1.py
my_dir//subdir2:
mod2.py
Обычный подход, объясненный до сих пор, заключался в добавлении путей
mydir, subdir1 и subdir2 к пути поиска модуля ( sys.path
), чтобы
иметь возможность импортировать mod0.py, mod1. py 'и' mod2.py '. Это
может привести к большим накладным расходам, если ваши модули
распределены по множеству разных подкаталогов, что обычно и имеет место.
В любом случае, импорт пакетов здесь, чтобы помочь. Они работают с
импортом названия самой папки.
Эта команда, например, недопустима и приведет к ошибке InvalidSyntax:
>> import /Users/Code/Projects/mydir/
File "<stdin>", line 1
import /Users/Code/Projects/mydir/
^
SyntaxError: invalid syntax
Правильный способ сделать это - установить только каталог контейнера '/
Users / Code / Projects /' в пути поиска вашего модуля (добавив его в
PYTHONPATH
или перечислить в файле .pth ), а затем импортировать
ваши модули, используя пунктирный синтаксис. Вот некоторые допустимые
импортные данные:
>> import mydir.mod0
>> import mydir.subdir1.mod1 as mod1
>> from mydir.subdir2.mod2 import print_name # print_name is a name defined within mod2.py
Вы, наверное, заметили ранее, что некоторые каталоги Python включают файл __init__.py. На самом деле это было требованием в Python2.x, чтобы сообщить Python, что ваш каталог является модульным пакетом. Файл __init__.py также является обычным файлом Python, который запускается всякий раз, когда этот каталог импортируется, и подходит для инициализации значений, например, для подключения к базе данных.
В любом случае, в большинстве случаев эти файлы остаются пустыми. В Python3.x эти файлы необязательны, и вы можете использовать их при необходимости. Следующие несколько строк показывают, как имена, определенные в __init__.py, становятся атрибутами импортируемого объекта (имя каталога, в котором он находится).
# __init__.py file in mydir/subdir1/ with code:
param = "init subdir1"
print(param)
# Import it from a Python interpreter
>> import mydir.subdir1.mod1
init subdir1
# param is also accessible as an attribute to mydir.subdir1 object
>> print(mydir.subdir1.param)
init subdir1
Еще одна важная тема, когда речь идет о пакетах модулей, - это относительный импорт . Относительный импорт полезен при импорте модулей внутри самого пакета. В этом случае Python будет искать импортированный модуль в пределах пакета, а не в пути поиска модуля.
Продемонстрируем один полезный случай на примере:
# mydir/subdir1/mod1.py
import mod2
# In Python interpreter:
>> import mydir.subdir1.mod1
ModuleNotFoundError: No module named 'mod2'
import mod2
сообщает Python, что нужно искать модуль mod2 в пути
поиска модуля, и, следовательно, это не удается. Вместо этого будет
работать относительный импорт. В следующем операторе относительного
импорта используется двойная точка (".."), которая обозначает
родительский элемент текущего пакета ('mydir /'). Следующий
подкаталог subdir2 должен быть включен для создания полного
относительного пути к модулю mod2 .
# mydir/subdir1/mod1.py
from ..subdir2 import mod2
Относительный импорт - огромная тема, которая может занять целую главу книги. Они также сильно различаются между версиями Python2.x и 3.x. На данный момент мы показали только один полезный случай, но в отдельных сообщениях блога должны быть другие, которым мы будем следовать.
Что касается Python 2.x, поддержка этой версии заканчивается в 2020 году , поэтому в случаях, когда между версиями Python есть большая разница, например, при относительном импорте, лучше сосредоточиться на версии 3.x.
Отправка пакета в PyPi
До сих пор вы научились писать модули Python, различать импортируемые модули и модули верхнего уровня, использовать определяемые пользователем модули в границах каталогов, изменять путь поиска модулей и, среди прочего, создавать / импортировать пакеты модулей. Создав полезное программное обеспечение, упакованное в пакет модулей, вы можете поделиться им с большим сообществом Python. В конце концов, Python создается и поддерживается сообществом.
Индекс пакетов Python (PyPI) - это
программный репозиторий для Python, который в настоящее время содержит
более 120 тыс. Пакетов (на момент написания этой статьи). Возможно, вы
ранее устанавливали модули из этого репозитория с помощью команды pip
.
Например, следующая строка загрузит и установит библиотеку Numpy для научных вычислений:
$ pip install numpy
Там больше информации об установке пакетов с пипом здесь . Но как вы вносите свой собственный пакет? Вот несколько шагов, которые помогут вам в этом.
- Во-первых, соблюдайте требования к упаковке и распространению. Здесь необходимо выполнить два шага:
|
|
$ pip install twine
- Следующим шагом будет настройка вашего проекта. В общем, это
означает добавление в проект нескольких файлов Python, которые будут
содержать информацию о конфигурации, руководства по использованию и
т. Д. PyPI предоставляет пример
проекта, который вы можете
использовать в качестве руководства. Вот самые важные файлы, которые
вам нужно добавить:
- setup.py: этот файл необходимо добавить в корень вашего проекта,
он служит интерфейсом командной строки для установки. Он должен
содержать функцию
setup()
которая будет принимать в качестве аргументов такую информацию, как: имя проекта, версия, описание, лицензия, зависимости проекта и т. Д. - README.rst: текстовый файл с описанием вашего пакета.
- licence.txt: текстовый файл, содержащий вашу лицензию на программное обеспечение. Подробнее о выборе лицензии читайте на GitHub.
- setup.py: этот файл необходимо добавить в корень вашего проекта,
он служит интерфейсом командной строки для установки. Он должен
содержать функцию
- Упакуйте свой проект. Наиболее часто используемый тип пакета - это «колесо», хотя вы также можете указать минимальное требование как «исходный дистрибутив / пакет». Здесь вам нужно использовать файл setup.py из предыдущего шага. Выполнение одной из следующих команд создаст каталог dist / в корне вашего проекта, содержащий файлы для загрузки в PyPI.
|
|
# Package as source distribution
$ python setup.py sdist
# Package as wheel supporting a single Python version
$ python setup.py bdist_wheel
- Последний шаг - загрузка вашего дистрибутива в PyPI. В основном
здесь два шага:
- Создайте учетную запись PyPI.
- Загрузите содержимое каталога dist /, созданного на предыдущем шаге. Здесь вы можете сначала загрузить тест с помощью тестового сайта PyPI .
|
|
$ twine upload dist/*
Это почти все. Для получения дополнительной информации на веб-сайте PyPI есть все подробные инструкции, если вы застряли.
Заключение
Этот пост был предназначен для того, чтобы направить вас от основных основ модулей Python (создание и импорт ваших первых импортируемых модулей) к более сложным темам (изменение пути поиска, пакетов модулей, перезагрузок и некоторых базовых относительных импортов) до отправки ваших Пакет Python в репозиторий программного обеспечения Python PyPI.
По этой теме много информации, и мы не смогли охватить все в этом одном сообщении, поэтому вы не сможете выполнить все эти шаги и отправить официальный пакет в течение времени чтения этого сообщения. Однако каждый шаг должен быть кратким введением, чтобы направлять вас на вашем пути обучения.