tgoop.com/advice17/24
Last Update:
Про импорты и структуру проекта
Когда вы импортируете какой-то модуль в вашем коде, питон не учитывает, в каком файле этот импорт находится, влияет только то, как был запущен код.
Если модуль не был раньше загружен, питон пытается его найти по очереди в нескольких папках, которые можно посмотреть в переменной sys.path
По умолчанию она содержит примерно такие каталоги (в некоторых ситуациях, например, при использовании embedded версии python, состав может отличаться):
* каталог, добавляемый при запуске
* каталоги указанные в переменной окружения PYTHONPATH
* каталог текущего активированного виртуального окружения
* каталог установки python
1. Если вы запускаете ваш скрипт командой python scriptname.py
, то первым в списке будет тот каталог, где находится запускаемый скрипт. Текущий каталог не имеет значения.
2. Если вы запускаете ваш код командой python -m packagename
, то первым в списке будет текущий каталог. При запуске питон попытается найти и импортировать packagename по общим правилам.
3. Если вы запускаете код с помощью других инструментов вроде pytest
, они тоже могут сами добавлять что-то в sys.path
.
Скорее всего, вам не стоит самостоятельно менять sys.path
, так как алгоритм его заполнения стандартный и привычен для всех. Если по каким-то причинам вас он не устраивает, возможно у вас неверная структура проекта.
Так как поиск пакетов для импорта происходит сначала в каталоге "проекта", стоит быть аккуратным именованием ваших файлов и каталогов. Если вы случайно назовете ваш модуль так же как встроенный или сторонний, при любом импорте такого модуля будет грузиться именно ваш, что сломает работу кода.
Иногда используемые нами фреймворки поддерживают только определенную, не всегда оптимальную, структуру проекта. В остальных случаях я могу предложить два подхода:
1. Вынести запускаемые скрипты на верхний уровень, а остальной код упаковать в пакет.
Упаковка кода в пакет с уникальным именем позволяет исключить конфликты имен. А вынесение всех запускаемых файлов на один уровень делает состав sys.path
предсказуемым.
Выглядеть это будет примерно так:
├── appname2. Создать распространяемый пакет (рекомендую).
│ ├── __init__.py
│ ├── other_module.py
│ └── some_module.py
├── cli_module.py
└── requirements.txt
В этом случае вы упаковываете весь код в пакет, что помогает исключить конфликты имен.
Для запуска команд вы можете использовать синтаксис
python -m appname.cli_module
или заполнить секцию entry_points
в файле с описанием проекта (setup.cfg
, pyproject.toml
), после чего иметь свои кастомные консольные команды. В обоих случаях вы сможете запускать код, находясь в любом каталоге, без необходимости указывать полные пути к файлам.Для удобства разработки с таким подходом удобно устанавливать пакет в editable-режиме с помощью команды типа
pip install -e .
Структура будет примерно такой:
├── pyproject.tomlДополнительные материалы:
└── src
└── appname
├── __init__.py
├── cli_module.py
├── other_module.py
└── some_module.py
* https://packaging.python.org/en/latest/
* https://docs.python.org/3/reference/import.html
* https://docs.python.org/3/library/sys.html#sys.path
* https://ru.wikipedia.org/wiki/Рабочий_каталог
BY Советы разработчикам (python и не только)
Share with your friend now:
tgoop.com/advice17/24