使用`import __main__`是一种好习惯吗?

bhe*_*ilr 28 python python-2.7

我正在研究一个相对较大的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()
Run Code Online (Sandbox Code Playgroud)
# 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
Run Code Online (Sandbox Code Playgroud)
# 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 ...
Run Code Online (Sandbox Code Playgroud)

这显然远远落后于我的应用程序实际上,并且忽略了错误处理,文档以及基本上所有实现细节.

现在,我已经看到它说这是一个坏主意,但没有解释为什么.由于大多数搜索"python import __main__"变体的结果都是关于什么的问题if __name__ == '__main__',因此很难找到关于这个主题的一些可靠信息.到目前为止,我没有任何问题,它实际上非常方便.

那么这被认为是很好的Python实践,还是有理由我应该避免这种设计?

Hen*_*ter 26

我认为有两个主要(哈哈)原因可能会规定避免这种模式.

  • 它会混淆您导入的变量的来源.
  • 如果您的程序有多个入口点,它会中断(或者至少很难维护).想象一下,如果有人(很可能是你)希望将某些功能的子集提取到一个独立的库中 - 他们必须删除或重新定义这些孤立引用中的每一个以使该东西在您的应用程序之外可用.

如果你有以上的应用程序总量控制,并永远不会有另一个切入点或其他使用您的功能,并且你确定你不介意的不确定性,我不认为有任何客观理由的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.
Run Code Online (Sandbox Code Playgroud)

然后你会更像这样使用它:

# main.py
import conf
import app

if __name__ == '__main__':
    import sys
    conf.init(sys.argv[1:])

    app.run()
Run Code Online (Sandbox Code Playgroud)
# app.py
import conf

def run():
    if conf.DEBUG:
        print('debug is on')
Run Code Online (Sandbox Code Playgroud)

注意使用conf.DEBUG而不是from conf import DEBUG.这种结构意味着您可以在程序的生命周期中更改变量,并将该更改反映在其他地方(显然,假设单个线程/进程).


另一个好处是,这是一个相当普遍的模式,因此其他开发人员将很容易认识到它.它很容易与settings.py各种流行应用程序(例如django)使用的文件相媲美,但我避免使用该特定名称,因为settings.py它通常是一堆静态对象,而不是运行时参数的命名空间.上面描述的配置命名空间模块的其他好名称可以是runtime或者params例如.


che*_*ner 7

这样做需要违反指定的PEP8

导入总是放在文件的顶部,就在任何模块注释和文档字符串之后,以及模块全局变量和常量之前.

为了gui.py成功导入__main__.DEBUG,你必须设置的值DEBUG 之前 import gui.