Пакеты и модули Python

В крупных Data Science проектах часто приходится сталкиваться с тем, что Python-приложение разрастается и его нужно как-то организовывать. В этой статье мы расскажем вам, как работать с пакетами и модулями Python: разберем запуск модулей, прямые и косвенные обращения, объединение в пакеты, относительные импорты и рекомендации PEP 8 по импорту.

Запуск модулей в командной строке

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

# Файл one.py
def foo(s):
    print(s)

foo("python")
# Файл two.py
import one
one.foo("school")

Строчка import one не только импортирует файл, но и запускает его, поэтому можно наблюдать двойной результат:

$ python one.py
python
$ python two.py
python
school

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

# Файл two.py
from one import foo

foo("school")
$ python two.py
python
school

В Python нет главной функции main

В отличие от многих языков программирования, в Python нет функции main(), которая запускает всю программу. Но, возможно, вам встречалось видеть __name__ == “__main__”. Дело в том, что Python создает переменную __name__, которой присваивается имя модуля такое, что:

  • если модуль запускается напрямую, то этой переменной будет присвоено __main__;
  • если модуль будет запущен через импорт, то ему будет присвоено само название модуля.

Допустим, у нас также имеется два файла, лежащие в одной папке:

# Файл one.py
def foo(s):
    print(s)

def bar():
    print(__name__)

if __name__ == "__main__":
    foo("python")
    bar()
# Файл two.py
import one

one.foo("school")
one.bar()

Запуск файла one.py присвоит переменной __name__ значение __main__, тогда этот блок с if сработает:

$ python one.py
python
__main__

Запуск файла two.py, который содержит импорт файла one.py не запустит блок с if, потому что название модуля соответствует one:

$ python two.py
school
one

В конструкцию __name__ == “__main__” вставляют код, который может быть запущен только в результате прямого обращения.

Объединяем модули в пакеты

Разбиение разного функционала по файлам — хорошая идея, поскольку в будущем будет удобно к ним обращаться. Именно такую иерархическую структуру имеют библиотеки и фреймворки: мы знаем, что, например, в tf.keras находится классы для создания архитектуры модели машинного обучения (Machine Learning). Обсудим, что нужно, чтобы организовать свои Python-файлы.

Объединение файлов под одним названием называется пакетом. Чтобы инициализировать такой пакет, достаточно создать пустой файл __init__.py в папке. Вот так, например, может выглядеть структура пакета:

ml/
    __init__.py
    supervised_learning/
        __init__.py
        decision_tree.py
        linear_reg.py
    unsupervised_learning/
        __init__.py
        pca.py
        k_means.py

Заметим, что сам пакет ml имеет также два других пакета, так как у каждого из них имеется файл __init__.py. Теперь импорты могут выглядеть следующим образом:

import ml.supervised_learning.decision_trees
from ml.supervised_learning import decision_trees
import ml.unsupervised_learning.pca as pca

Эти три строчки примеры абсолютного импорта.

Относительные импорты внутри пакета

Импорт модулей может быть также и относительным. Обращаясь к предыдущему примеру с пакетом ml, требуется в модуле unsupervised_learning.pca импортировать k_means. Это выглядит так:

# внутри ml/unsupervised_learning/pca.py
from . import k_means

А чтобы из этого же модуля импортировать supervised_learning.linear_reg, нужно добавить две точки:

# внутри ml/unsupervised_learning/pca.py
from ..supervised_learning import linear_reg

Относительные импорты должны содержать ключевое слово from. Ниже рассмотрены правильные и неправильные варианты.

# внутри ml/unsupervised_learning/pca.py
from . import k_means                        # ОК (относительный импорт)
from ml.unsupervised_learning import k_means # ОК (абсолютный импорт)
import k_means                               # Ошибка
import .k_means                              # Ошибка

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

Наконец, стоит отметить, что относительные импорты работают только для модулей надлежащего пакета, поэтому попытка обратиться к ним со стороны может оказаться неудачей. Но, чтобы обойти эту проблему, нужно указать флаг -m и запустить в виде модуля. Примеры ниже иллюстрируют правильный и неправильный способ запуска модулей со стороны, которые содержат относительные импорты.

$ python ml/unsupervised_learning/pca.py # Ошибка относительных импортов
$ python -m ml.unsupervised_learning.pca # ОК

Рекомендации PEP 8

Здесь мы также приведем примеры импорта согласно PEP 8 — документ с рекомендациями по стилю кодированию на Python.

  • Импорт отдельных модулей/пакетов должны осуществляться на разных строчках:
    # Правильно
    import os
    import sys
    
    # Неправильно
    import os, sys
    

    Тем не менее, в порядке вещей импортировать объекты из одного модуля/пакета:

    # Правильно
    from subprocess import Popen, PIPE
    
  • Импорты должны находится вверху файла, после комментариев к модулям, после документации и перед глобальными переменными и константами.
  • Все “dunders”, то есть все объекты Pythonс двумя нижними подчеркиваниям, должны находиться перед импортами:
    """Это документация к модулю
    
    Все dunders находятся перед импортами.
    """
    
    from __future__ import barry_as_FLUFL
    
    __all__ = ['a', 'b', 'c']
    __version__ = '0.1'
    __author__ = 'Cardinal Biggles'
    
    import os
    import sys
  • Импорты должны быть сгруппированы по следующему порядку:
    1. Стандартные Python-библиотеки.
    2. Связанные сторонние библиотеки.
    3. Локальные пакеты/модули.

    Пустая строка должна быть после каждой группы импортов.

  • Рекомендуется использовать абсолютные импорты, так как они более читаемы и отлаживаемы:
    import mypkg.sibling
    from mypkg import sibling
    from mypkg.sibling import example
    

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

  • Конструкции from <module> import *, которая импортирует все из модуля, нужно избегать, так как есть вероятность, что переменные импортированного модуля и переменные настоящего модуля будут иметь схожие названия.

В следующей статье поговорим о тестировании кода на Python. А как на практике эффективно организовать файлы с вашими Data Science проектами, вы узнаете на наших Python-курсах в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.

Добавить комментарий

Поиск по сайту