Правильно ли использовать "import __main__"?
Я работаю над относительно большим Python-приложением, и есть несколько ресурсов, которые я хотел бы сохранить, поскольку глобальные переменные доступны в нескольких разных модулях. Эти значения такие, как номер версии, дата версии, глобальная конфигурация и некоторые статические пути к ресурсам. Я также добавил флаг DEBUG
, который устанавливается параметром командной строки, чтобы я мог запускать свое приложение в режиме отладки без необходимости в полной среде.
Значения, которые я импортирую, я был осторожен, чтобы гарантировать, что это те, которые не меняются в ходе запуска программы, и я документировал их как глобальные постоянные переменные, которые не следует трогать. Мой код выглядит по существу как
# Main.py
import wx
from gui import Gui
DEBUG = False
GLOBAL_CONFIG = None
VERSION = '1.0'
ICON_PATH = 'some/path/to/the/app.ico'
def main():
global DEBUG, GLOBAL_CONFIG
# Simplified
import sys
DEBUG = '--debug' in sys.argv
GLOBAL_CONFIG = load_global_config()
# Other set-up for the application, e.g. setting up logging, configs, etc
app = wx.App()
gui = Gui()
app.MainLoop()
if __name__ == '__main__':
main()
# gui.py
import wx
from __main__ import DEBUG, GLOBAL_CONFIG, ICON_PATH
import controller
class Gui(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
icon = wx.Icon(ICON_PATH, wx.BITMAP_TYPE_ICO)
self.SetIcon(icon)
# Always make a copy so we don't accidentally modify it
conf = GLOBAL_CONFIG.copy()
self.controller = controller.Controller(conf)
# More setup, building the layout, etc
# controller.py
from __main__ import DEBUG
import logging
log = logging.getLogger('controller')
class Controller(object):
def __init__(self, conf):
if DEBUG:
log.info("Initializing controller in DEBUG mode")
self.conf = conf
# Other setup ...
Это, безусловно, значительно урезано от того, что на самом деле является моим приложением, и пренебрегает обработкой ошибок, документацией и в основном всеми деталями реализации.
Теперь я видел, как он сказал, что это плохая идея, но без объяснения причин. Поскольку большинство результатов, когда googling для вариантов "python import __main__" - это вопросы о том, что if __name__ == '__main__'
, трудно найти твердую информацию по этой теме. До сих пор у меня не было никаких проблем с этим, и на самом деле это было действительно удобно.
Итак, это считается хорошей практикой на Python, или есть причина, по которой мне следует избегать этого дизайна?
Ответы
Ответ 1
Я думаю, что есть две основные (ха-ха) причины, которые можно было бы предписать избегать этого шаблона.
- Он запутывает происхождение импортируемых переменных.
- Это ломается (или, по крайней мере, сложно поддерживать), если ваша программа имеет несколько точек входа. Представьте себе, если кто-то, возможно, захотел извлечь часть подмножества ваших функций в автономную библиотеку - им нужно было бы удалить или переопределить каждую из этих сиротских ссылок, чтобы сделать эту вещь полезной вне вашего приложения.
Если у вас есть полный контроль над приложением, и никогда не будет другой точки входа или другого использования для ваших функций, и вы уверены, что не возражаете против двусмысленности, я не думаю, что есть объективная причина, по которой from __main__ import foo
шаблон плохой. Мне это не нравится лично, но опять же, это в основном по двум причинам выше.
Я думаю, что более надежное решение для разработчиков может быть чем-то вроде этого, создавая специальный модуль специально для хранения этих суперглобальных переменных. Затем вы можете импортировать модуль и обратиться к module.VAR
в любое время, когда вам нужно установить этот параметр. По сути, просто создайте специальное пространство имен модулей, в котором будет храниться супер-глобальная конфигурация времени выполнения.
# conf.py (for example)
# This module holds all the "super-global" stuff.
def init(args):
global DEBUG
DEBUG = '--debug' in args
# set up other global vars here.
Затем вы будете использовать его более как это:
# main.py
import conf
import app
if __name__ == '__main__':
import sys
conf.init(sys.argv[1:])
app.run()
# app.py
import conf
def run():
if conf.DEBUG:
print('debug is on')
Обратите внимание на использование conf.DEBUG
, а не from conf import DEBUG
. Эта конструкция означает, что вы можете изменить переменную в течение срока действия программы, и это изменение отразилось в другом месте (при условии, что один поток/процесс, очевидно).
Еще один недостаток заключается в том, что это довольно распространенный шаблон, поэтому другие разработчики с готовностью узнают его. Это легко сопоставимо с файлом settings.py
, используемым различными популярными приложениями (например, django
), хотя я избегал этого конкретного имени, потому что settings.py
обычно является кучей статических объектов, а не пространством имен для параметров времени исполнения. Другие хорошие имена для модуля пространства имен конфигурации, описанного выше, могут быть, например, runtime
или params
.
Ответ 2
Для этого требуется нарушение PEP8, в котором указывается
Импорт всегда помещается в верхнюю часть файла сразу после комментариев и док-строк модуля, а также перед глобалями и константами модуля.
Чтобы gui.py
успешно импортировать __main__.DEBUG
, вам нужно установить значение DEBUG
до import gui
.