Обычно код в больших проектах делят на модули — отдельные файлы, в которых хранится код на Python. Но иногда и этого разделения недостаточно. Часто модули хочется сгруппировать «по смыслу» или сформировать отдельную группу модулей, чтобы использовать их в других проектах.
В таких случаях помогают пакеты — группы модулей. В особенно больших проектах используются еще и подпакеты внутри пакетов, но на этой теме мы пока не будем останавливаться. В этом уроке разберемся, как создавать пакеты и добавлять в них модули. Также поговорим о том, какими способами можно импортировать пакеты в проект.
Создаем пакет
С точки зрения структуры пакет — это директория с файлами модулей, с названием в формате snake_case и содержащая специальный модуль с именем __init__.py
. Именно наличие этого специального файла подсказывает интерпретатору Python, что директорию следует воспринимать как пакет.
Рассмотрим пример самого простого пакета. Создадим пакет из директории package
и модуля __init__.py
:
package/
└── __init__.py
В файл __init__.py
добавим следующий код:
# file __init__.py
NAME = "super_package"
Теперь у нас есть небольшой, но уже полноценный пакет. Его можно импортировать так же как модуль:
import package
print(package.NAME)
Обратите внимание, что отдельно импортировать файл __init__.py
не нужно. При первом обращении к пакету Python самостоятельно импортирует модуль __init__.py
. Это происходит автоматически, потому что директория без init-файла не будет считаться пакетом.
Добавляем модули в пакет
С простым пакетом все ясно — его можно использовать как модуль. Теперь перейдем к группировке. Добавим в пакет еще два модуля:
package/
├── constants.py
├── functions.py
└── __init__.py
Чтобы было понятнее, рассмотрим содержимое модуля constants.py
:
# file constants.py
PERSON = "Alice"
И содержимое модуля functions.py
:
# file functions.py
def greet(who: str) -> None:
print("Hello, " + who + "!")
Теперь в пакете есть не только __init__.py
, но и еще два модуля — теперь их можно импортировать.
Как импортировать модули из пакетов
Импорт модулей из пакета выглядит так же, но к имени модуля приписывается имя пакета. То есть указывается путь до модуля.
Например, импорт всего модуля выглядит так:
import package.functions
import package.constants
package.functions.greet(package.constants.PERSON) # => Hello, Alice!
К названию модуля functions
приставляется название пакета package
, где этот модуль и лежит. Обратите внимание, что путь указывается через точку.
Попробуем импортировать отдельные определения, то есть саму функцию и аргумент:
from package.functions import greet
from package.constants import PERSON
greet(PERSON) # => Hello, Alice!
При импорте модуля из пакета всегда важно указывать путь. В Python существует два способа его указания: абсолютный и относительный. В предыдущих уроках мы рассматривали импорт модулей без этого разделения. Но для понимания пакетов оно критически важно, поэтому здесь обсудим подробности.
Абсолютный импорт
В абсолютном импорте нужно прописывать полный путь до модуля, включающий все пакеты и подпакеты. Полные пути гарантируют простоту чтения и однозначность — так всем будет понятно, что и откуда импортируется. Чтобы вам было удобнее читать код, во всех примерах выше мы использовали абсолютный импорт.
package/
├── constants.py
├── functions.py
└── __init__.py
Внутри директории package лежит модуль constants.py. При абсолютном импорте мы укажем полный путь до него - package.constants
.
from package.functions import greet
Относительный импорт
В относительном импорте мы указываем путь не от самого корня проекта, а относительно модуля в котором будет строчка импорта. Для обозначения текущего модуля используется точка, которая означает импорт модуля из текущей директории.
package/
├── constants.py
├── functions.py
└── __init__.py
Например, мы работаем с файлом functions.py и хотим импортировать constants.py. При этом functions.py и constants.py хранятся в одной и той же директории. Тогда мы можем указать импорт как from . import constants
. Что значит "из текущей директории импортируй модуль constants".
# functions.py
from . import constants
Или для импорта отдельных определений:
# functions.py
from .constants import PERSON
Относительный импорт помогает писать быстрее, но слишком сильно запутывает код, что негативно сказывается на читаемости. Именно поэтому в сообществе Python-разработчиков есть распространенный совет для новичков: старайтесь пользоваться абсолютным импортом, даже в самых простых и очевидных случаях.