Рубрики
Без рубрики

Модули Python: Создание, импорт и совместное использование

Автор оригинала: Mihajlo Pavloski.

Модули Python: Создание, импорт и совместное использование

Вступление

Модули – это организационная единица самого высокого уровня в Python. Если вы хотя бы немного знакомы с Python, вы, вероятно, не только использовали готовые модули, но и создали несколько самостоятельно. Так что же такое модуль? Modules -это блоки, которые хранят код и данные, обеспечивают повторное использование кода в проектах Python, а также полезны при разбиении пространств имен системы на автономные пакеты. Они автономны, потому что вы можете получить доступ к атрибутам модуля только после его импорта. Вы также можете понимать их как пакеты имен, которые при импорте становятся атрибутами импортируемого объекта модуля. Фактически, любой файл Python с расширением .py представляет собой модуль.

В этой статье мы начнем с основных основ создания и импорта модулей, перейдем к более продвинутым случаям использования модулей, к упаковке и отправке ваших модулей в “официальный” репозиторий программного обеспечения Python, структурированный соответственно на три части: Создание модуля , Использование модуля и Отправка пакета в PyPI .

Создание модуля

Основы

На самом деле в создании модуля Python нет особой философии, поскольку файлы с суффиксом .py представляют собой модуль. Хотя не каждый файл Python предназначен для импорта в виде модуля. Файлы Python, которые используются для запуска в качестве автономного приложения Python (файлы верхнего уровня), обычно предназначены для запуска в виде скриптов, и их импорт фактически запускает команды в скрипте.

Модули, предназначенные для импорта другим кодом, не будут выполнять какой-либо код, а только предоставлять его имена верхнего уровня в качестве атрибутов импортируемому объекту. Также можно разработать двухрежимный код модули Python, которые можно было бы использовать как для импорта, так и для запуска в качестве скрипта верхнего уровня.

В то время как правила создания модулей довольно расслаблены, существует одно правило именования модулей. Поскольку имена файлов модулей становятся именами переменных в Python при импорте, не разрешается называть модули зарезервированными словами Python. Например, a for.py модуль может быть создан, но не может быть импортирован, потому что “for” – это зарезервированное слово. Давайте проиллюстрируем то, что мы уже упоминали, на примере “Привет, мир!”.

# 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

Как отмечалось ранее, важным выводом из этого первого базового примера является то, что имена файлов модулей важны. После импорта они становятся переменными/объектами в модуле импортера. Все определения кода верхнего уровня в модуле становятся атрибутами этой переменной.

Под “верхним уровнем” я подразумеваю любую функцию или переменную, которая не вложена в другую функцию или класс. Затем доступ к этим атрибутам можно получить с помощью стандартного оператора . в Python.

В следующем разделе мы сначала рассмотрим “общую картину” многофайловых программ Python, а затем в “двойном режиме” файлы Python.

Архитектура программы

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

  • файл верхнего уровня : Файл Python или скрипт , который является основной точкой входа программы. Этот файл запускается для запуска вашего приложения.
  • пользовательские модули : файлы Python, которые импортируются в файл верхнего уровня или между собой и предоставляют отдельные функциональные возможности. Эти файлы обычно не запускаются непосредственно из командной строки и создаются специально для целей проекта.
  • стандартные библиотечные модули : Предварительно закодированные модули, встроенные в установочный пакет Python, такие как платформенно-независимые инструменты для системных интерфейсов, интернет-скрипты, построение графического интерфейса и другие. Эти модули являются не частью самого исполняемого файла Python, а частью стандартной библиотеки Python .

На рис. 1 показан пример структуры программы с тремя типами файлов:

Структура программного модуля Python

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

На этом рисунке модуль ‘top_module.py’-это файл Python верхнего уровня, который импортирует инструменты, определенные в модуле ‘module 1’, но также имеет доступ к инструментам в ‘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(n-k)


# module2.py
import module1
k = 2.0
def print_parameters():
    print('k = %.f n = %.f' % (k, module1.n))

В приведенном выше примере ‘top_module.py’-это модуль верхнего уровня, который будет запускаться пользователем, и он импортирует инструменты из других модулей через ‘module1.py”. module1 и module2 являются пользовательскими модулями, в то время как модуль ‘math’ импортируется из стандартной библиотеки Python. При запуске скрипта верхнего уровня мы получаем:

$ python top_module.py
k = 2 n = 5
10.0

При запуске файла Python верхнего уровня его операторы исходного кода и операторы внутри импортированных модулей компилируются в промежуточном формате , известном как byte code , который является независимым от платформы форматом. Файлы байтового кода импортированных модулей хранятся с расширением .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’ is imported it won’t run the code nested under the if __name__ statement.

# Terminal window

$ python hiprinter.py
Hi!Hi!Hi!
# Python interpreter

>> import hiprinter
>> hiprinter.print_hi()
Hi!Hi!Hi!

Двухрежимный код очень распространен на практике и особенно полезен для модульного тестирования: в то время как переменные и функции определяются как имена верхнего уровня в файле, часть внутри оператора if может служить областью тестирования указанных выше имен.

Использование модуля

Инструкции по импорту

Пример в разделе Архитектура программы был полезен для рассмотрения разницы между двумя операторами импорта: import и from . Основное отличие заключается в том, что import загружает весь модуль как единый объект, в то время как from загружает определенные свойства и функции из модуля. Импорт имен с помощью оператора from затем можно использовать непосредственно в модуле importer, не вызывая имя импортируемого объекта.

Использование оператора from разрешено только на верхнем уровне файла модуля в Python 3.x, а не внутри функции. Python 2.x позволяет использовать его в функции, но выдает предупреждение. С точки зрения производительности оператор from работает медленнее, чем import , поскольку он выполняет всю работу, которую выполняет import -просматривает все содержимое импортируемого модуля, а затем делает дополнительный шаг в выборе подходящих имен для импорта.

Существует также третий оператор импорта from* , который используется для импорта всех имен верхнего уровня из импортированного модуля и использования их непосредственно в классе importer. Например, мы могли бы использовать:

from module2 import *

Это позволит импортировать все имена (переменные и функции) из module2.py файл. Такой подход не рекомендуется из – за возможного дублирования имен-импортированные имена могут перезаписать уже существующие имена в модуле импортера.

Путь Поиска модуля

Одним из важных аспектов при написании модульных приложений Python является поиск модулей, которые необходимо импортировать. В то время как модули стандартной библиотеки Python настроены на глобальный доступ, импорт пользовательских модулей через границы каталогов может стать более сложным.

Python использует список каталогов, в которых он ищет модули, известный как путь поиска . Путь поиска состоит из каталогов, найденных в следующих каталогах:

  1. Домашний каталог программы. Расположение скрипта верхнего уровня. Обратите внимание, что домашний каталог может не совпадать с текущим рабочим каталогом . PYTHONPATH
  2. каталоги. Если задано, переменная среды PYTHONPATH определяет конкатенацию пользовательских каталогов, в которых интерпретатор Python должен искать модули. Стандартные библиотечные каталоги. Эти каталоги автоматически устанавливаются при установке Python и всегда ищутся.
  3. Каталоги, перечисленные в файле
  4. .the files. Эта опция является альтернативой PYTHONPATH , и она работает путем добавления ваших каталогов, по одному на строку, в текстовый файл с суффиксом .pth , который должен быть помещен в каталог установки Python, который обычно является /usr/local/lib/python3.6/| на машине Unix или C:\Python36\ на машине с Windows. Каталог site-packages
  5. . В этот каталог автоматически добавляются все сторонние расширения.

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

$ echo $PYTHONPATH
/Users/Code/Projects/:

Чтобы создать переменную на компьютере с Windows, вы должны использовать инструкции в разделе “Панель управления -> Система -> Дополнительно”, в то время как на macOS и других Unix-системах проще всего добавить следующую строку либо к ~/.bashrc , либо к ~/.bash_profile файлам, где ваши каталоги объединены знаком двоеточия (“:”).

export PYTHONPATH=:$PYTHONPATH".

Этот метод очень похож на добавление каталогов в ваш Unix $PATH .

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

В любом случае параметры PYTHONPATH и .path позволяют более постоянно изменять путь поиска. Важно знать, что 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)

>> print(my_module.name)
Jack

Функция reload изменяет модуль на месте. То есть, не затрагивая другие объекты, которые ссылаются на импортированный модуль. Вы можете заметить, что функция также возвращает сам модуль, давая его имя и путь к файлу. Эта функция особенно полезна на этапе разработки, но также и в более крупных проектах.

Например, для программ, которым требуется постоянное подключение к серверу, перезапуск всего приложения обходится гораздо дороже, чем динамическая перезагрузка или горячая перезагрузка для использования во время разработки.

Пакеты модулей

При импорте имен модулей вы фактически загружаете файлы Python, хранящиеся где-то в вашей файловой системе. Как упоминалось ранее, импортированные модули должны находиться в каталоге, который указан в пути поиска вашего модуля ( sys.path ). В Python есть нечто большее, чем просто “импорт имен” – вы действительно можете импортировать целый каталог, содержащий файлы Python, как пакет модуля . Этот импорт известен как package imports .

Итак, как вы импортируете пакеты модулей? Давайте создадим каталог с именем ‘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”. Это может стать большим накладным расходом, если ваши модули распределены по многим различным подкаталогам, что обычно и происходит. В любом случае, импорт пакетов здесь, чтобы помочь. Они работают с импортом имени самой папки.

Эта команда, например, не разрешена и приведет к Неверный синтаксис ошибка:

>> import /Users/Code/Projects/mydir/
  File "", 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 file также является обычным файлом 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 искать модуль “mod 2” в пути поиска модуля, и поэтому он не удался. Вместо этого относительный импорт будет работать просто отлично. Следующий оператор относительного импорта использует двойную точку ( ” .. ” ), которая обозначает родительский элемент текущего пакета (“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 можно найти здесь . Но как вы вносите свой собственный пакет? Вот несколько шагов, которые помогут вам в этом.

  • Во-первых, удовлетворить требования к упаковке и дистрибуции. Здесь необходимо сделать два шага:
    • Установите pip, setuptools и wheel. Более подробная информация об этом здесь .
    • Установите шпагат , который используется для загрузки вашего проекта в PyPI
$ pip install twine
  • Следующий шаг-настройка вашего проекта. В общем случае это означает добавление нескольких файлов Python в ваш проект, которые будут содержать информацию о конфигурации, руководства по использованию и т. Д. PyPI предоставляет пример пример проекта , который вы можете использовать в качестве руководства. Вот самые важные файлы, которые вам нужно добавить:

    • setup.py: Этот файл должен быть добавлен в корень вашего проекта и служит интерфейсом командной строки установки. Он должен содержать функцию setup () , которая будет принимать в качестве аргументов такую информацию, как имя проекта, версия, описание, лицензия, зависимости проекта и т. Д.
    • README.rst: Текстовый файл, описывающий ваш пакет.
    • licence.txt: Текстовый файл, содержащий вашу лицензию на программное обеспечение. Более подробная информация о выборе лицензии , через GitHub.
  • Упакуйте свой проект. Наиболее часто используемый тип пакета – “колесо”, хотя вы также можете указать минимальное требование как “исходный дистрибутив/пакет”. Здесь вам нужно использовать ‘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.

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

Повторные ссылки