LINUX.ORG.RU

Правильная архитектура программы в Python

 


2

3

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

Возникла потребность вторично использовать уже написанный код. Можно, конечно, делать

from main import some_function
но есть одна заковырка. Мне необходимо увеличить скорость импорта из главного модуля. При этом возникают следующие вопросы:

1) Что делать с внешними модулями (os, sys, difflib, pyperclip, zipfile, rarfile, pyunpack, codecs, re, time, shutil, webbrowser...)? Если подгружать все сразу, то даже импорт какого-нибудь примитива из главного модуля займет несколько секунд из-за подгрузки всех внешних модулей. Если же подгружать модули в случае необходимости, то, во-первых, как проверить их существование на самом старте и сообщить об этом, а не ловить ошибки посреди работы, а во-вторых, придется во все функции вставлять import, что не очень хорошо хотя бы потому, что одни и те же модули могут загружаться несколько раз (хотя я и не знаю, как это реализовано, может, python умный и не делает импорт из тех же самых модулей по нескольку раз).

2) Программа использует кучу глобальных переменных, которые подгружаются из внешнего файла посредством parser.get (.getint, .getfloat и т.д.). Возникает такой же вопрос, как и с модулями. Кроме того, как мне объявлять эти переменные? Просто в теле главного модуля? Тогда каждый мало-мальский импорт из главного модуля потребует наличия конфигурационного файла. А если в виде функции, то как сделать загружаемые из конфига переменные глобальными? Большинство переменных нужны на чтение, но некоторые нужны и на запись. Бывалые питонисты не рекомендуют использовать global. Кроме того, рекомендуется объявлять переменные явно, а не через exec и eval, потому что это угроза безопасности. Но при этом опять оказывается необходимым тащить с собой конфиг.

3) В каждой функции, чтобы иметь возможность контролировать ее результат, я использую логирование. Логирование также может использовать внешние модули (например, os), а также некоторые глобальные переменные (на чтение). Что с этим делать? Поставить заглушку, если условие if __name__ == '__main__' не выполняется?

4) Я не особо опытен, программирую на питоне год или полтора для собственных нужд. На данный момент весь код стремлюсь оформлять в виде функций. Нужно ли переходить на классы или это просто разные парадигмы?

Deleted
Ответ на: комментарий от Deleted

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

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

По остальному:

придется во все функции вставлять import, что не очень хорошо хотя бы потому, что одни и те же модули могут загружаться несколько раз (хотя я и не знаю, как это реализовано, может, python умный и не делает импорт из тех же самых модулей по нескольку раз).

Да, питон умный, и кэширует модули, но такой подход считается плохой практикой (хотя есть и сторонники). В том же PEP8 рекомендуют всё импортировать в начале файла.

3) В каждой функции, чтобы иметь возможность контролировать ее результат, я использую логирование. Логирование также может использовать внешние модули (например, os), а также некоторые глобальные переменные (на чтение). Что с этим делать? Поставить заглушку, если условие if __name__ == '__main__' не выполняется?

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

На данный момент весь код стремлюсь оформлять в виде функций. Нужно ли переходить на классы или это просто разные парадигмы?

Не обязательно, но, возможно, это решит часть твоих проблем. Правильное применение наследования и прочих фишек ООП, например, может сделать код лучше и универсальнее. Но это зависит от конкретной ситуации, плюс нужно будет продумать всё как следует, иначе код с классами будет монструозным и запутанным.

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

risenshnobel ★★★
()
Ответ на: комментарий от risenshnobel

В том же PEP8 рекомендуют всё импортировать в начале файла.

Ясно, спасибо.

В питоне есть достаточно гибкий модуль logging, который можно настраивать множеством способов.

logging, как выяснилось, по умолчанию пишет в системной кодировке, даже если файл изначально создавался в UTF-8. Возможно, это как-то настраивается, но мне в любом случае надо было понавешать сверху много условий, поэтому я ограничился write и print. Впрочем, это наименьшая из проблем. Мне достаточно указать в программе UseLog=False, и функция у меня задействована не будет.

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.