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

时间:2014-06-03 19:42:10

标签: 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()

# 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 ...

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

现在,我seen it said这是一个坏主意,但没有解释原因。搜索" python import __main __"的变体时搜索结果最多。关于if __name__ == '__main__'是什么的问题,很难找到关于这个主题的一些可靠信息。到目前为止,我没有遇到任何问题,而且实际上非常方便。

所以这被认为是优秀的Python实践,还是有理由避免这种设计?

2 个答案:

答案 0 :(得分:23)

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

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

如果您完全掌控了该应用程序,并且您的功能将永远不会有其他入口点或其他用途,并且您确定不会注意到这种模糊性,我不会想到有{em>目标的原因导致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通常是一堆静态对象,而不是运行时参数的命名空间。例如,上述配置命名空间模块的其他好名称可能是runtimeparams

答案 1 :(得分:6)

这样做需要违反PEP8,指定

  

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

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