Является ли хорошей практикой использование `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 ...
Это явно далеко от того, что на самом деле представляет собой мое приложение, и пренебрегает обработкой ошибок, документацией и, в основном, всеми деталями реализации.
Я видел, что это говорит о том, что это плохая идея, но без объяснения причин. Поскольку большинство результатов при поиске вариантов "python import __main__" - это вопросы о том, что if __name__ == '__main__'
Это трудно найти надежную информацию по этой теме. До сих пор у меня не было проблем с этим, и на самом деле это было действительно удобно.
Так это считается хорошей практикой Python, или есть причина, по которой я должен избегать этого дизайна?
2 ответа
Я думаю, что есть две основные (ха-ха) причины, по которым можно было бы предписать избегание этого паттерна.
- Он запутывает происхождение переменных, которые вы импортируете.
- Он ломается (или, по крайней мере, трудно поддерживать), если ваша программа имеет несколько точек входа. Представьте себе, если бы кто-то, возможно, вы захотели извлечь какое-то подмножество ваших функций в автономную библиотеку - им пришлось бы удалить или переопределить каждую из этих потерянных ссылок, чтобы сделать ее пригодной для использования вне вашего приложения.
Если у вас есть полный контроль над приложением, и никогда не будет другой точки входа или другого использования ваших функций, и вы уверены, что не против двусмысленности, я не думаю, что есть какая-либо объективная причина, почему 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
, например.
Это требует нарушения PEP8, в котором указано
Импорт всегда помещается в верхнюю часть файла, сразу после любых комментариев модуля и строк документации, а также перед глобальными переменными и константами модуля.
Для того чтобы gui.py
успешно импортировать __main__.DEBUG
вам нужно будет установить значение DEBUG
до import gui
,