Подтвердить что ты не робот

Правильно ли использовать "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, или есть причина, по которой мне следует избегать этого дизайна?

4b9b3361

Ответ 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.