Python: совместное использование общего кода среди семейства скриптов
Я пишу семейство скриптов Python в рамках проекта; каждый script находится в подкаталоге проекта, например:
projectroot
|
|- subproject1
| |
| |- script1.main.py
| `- script1.merger.py
|
|- subproject2
| |
| |- script2.main.py
| |- script2.matcher.py
| `- script2.merger.py
|
`- subproject3
|
|- script3.main.py
|- script3.converter.py
|- script3.matcher.py
`- script3.merger.py
Теперь некоторые из скриптов имеют общий код. Общий код лучше всего рассматривать как часть самого проекта, а не то, что я собирал бы отдельно, и выкладывал бы библиотеку, или удалял бы PYTHONPATH. Я мог бы разместить этот код в разных местах, например, в самом каталоге projectroot
или в дочернем каталоге projectroot
, который называется common
(возможно).
Однако большинство способов, о которых я думал до сих пор, включают в себя создание пакетов из моих подпроектов с пустыми __init__.py
файлами и использование относительного импорта (или избыточное возиться с sys.path
в каждом подпроекте. Хуже, похоже, структура пакета вокруг этого семейства скриптов запускается из следующего предупреждения от отклоненного PEP-32122:
Внимание! Этот PEP был отклонен. Guido просматривает сценарии в пакете как анти-шаблон.
Если скрипты внутри пакета являются анти-патчавыми, как я могу настроить вещи таким образом, чтобы сохранить общий код в одном проекте? Или это приемлемый здесь модуль и пакетная система? Какой самый чистый подход? (FWIW Я предпочел бы иметь файл, такой как shared.py
или common.py
в корневом каталоге проекта, вместо того, чтобы сделать каталог утилиты, который является родным для "реальных" подпроектов.)
Ответы
Ответ 1
Я предлагаю положить тривиальные сценарии "запуска" на верхнем уровне вашего проекта и сделать каждую из подпроектов в пакеты. Модули в пакетах могут импортировать друг друга, или общий код может быть учтен в пакете common
.
Здесь будет выглядеть структура, если предположить, что различные модули merger
могут быть реорганизованы в общую версию:
projectroot
|- script1.py # launcher scripts, see below for example code
|- script2.py
|- script3.py
|
|- common
| |- __init__.py
| |- merger.py # from other packages, use from ..common import merger to get this
|
|- subproject1
| |- __init__.py # this can be empty
| |- script1_main.py
|
|- subproject2
| |- __init__.py
| |- script2_main.py
| |- script2_matcher.py
|
|- subproject3
|- __init__.py
|- script3_main.py
|- script3_converter.py
|- script3_matcher.py
Скрипты запуска могут быть очень простыми:
from subproject1 import script1_main
if __name__ == "__main__":
script1_main.main()
То есть все, что он делает, это импортировать соответствующий модуль "scriptN_main" и запустить в нем функцию. Использование простого script может также иметь некоторые небольшие преимущества для скорости запуска script, так как модуль main
может иметь свой скомпилированный байт-код в кешированном файле .pyc
, тогда как скрипты никогда не кэшируются.
Примечание. Я переименовал ваши модули, заменив _
символы на символы .
. Вы не можете иметь .
в идентификаторе (например, имя модуля), поскольку Python ожидает, что он укажет доступ к атрибуту. Это означает, что эти модули никогда не могут быть импортированы. (Я предполагаю, что это артефакт только файлов примеров, а не то, что у вас есть в вашем реальном коде.)
Ответ 2
Мое предпочтение было бы отдельным каталогом "bin" или "scripts" с подпроектами в виде библиотек/пакетов:
projectroot
|
|- scripts
|
|- lib
| |
| `- matcher.py
| `- merger.py
| `- subproject1
| `- subproject2
| `- subproject3
Идея ваших сценариев может ссылаться на любые подпроекты, необходимые как обычные пакеты. И ваши подпроекты также могут ссылаться друг на друга с импортом.
Затем у вас также может быть основной или общий script, который устанавливает для вас пакеты подпроектов, если это помогает.
Ответ 3
Пожалуйста, используйте setuptools
, чтобы распределить библиотеки скриптов и:
например.
from setuptools import setup
setup(
# other arguments here... (e.g. packages / package_dir)
entry_points = {
'console_scripts': [
'script1 = subproject1.script1:main',
'script2 = subproject2.script2:main',
],
}
)
Если вы можете написать все свои коды в виде библиотек и не нуждаться в отдельных модулях, чтобы иметь свои точки входа, то это инструмент для вас. Если у вас есть скрипты, это тоже хорошо, но вам понадобится функция main
, которую вы можете ссылаться (см. Пример выше)