使用 `import __main__` 是好习惯吗? [英] Is it good practice to use `import __main__`?

查看:94
本文介绍了使用 `import __main__` 是好习惯吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个相对较大的 Python 应用程序,我希望将一些资源作为全局变量保留在多个不同的模块中.这些值包括版本号、版本日期、全局配置和一些资源的静态路径.我还包含了一个由命令行选项设置的 DEBUG 标志,以便我可以在调试模式下运行我的应用程序,而无需完整环境.

I'm working on a relatively large Python application, and there are several resources that I would like to keep as global variables accessible throughout several different modules. These values are things like the version number, version date, the global configuration, and some static paths to resources. I've also included a DEBUG flag that gets set by a command line option so that I can run my application in a debug mode without needing the full environment.

我正在导入的值我一直小心翼翼地确保在运行程序的过程中不会改变,并且我已经将它们记录为不应触及的全局常量变量.我的代码看起来基本上像

The values I'm importing I've been careful to ensure are ones that do not change over the course of running the program, and I've documented them as global constant variables that should not be touched. My code looks essentially like

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

<小时>

这显然与我的应用程序的实际情况相去甚远,并且忽略了错误处理、文档和基本上所有的实现细节.


This is obviously far stripped down from what my application actually is, and neglects error handling, documentation, and basically all implementation details.

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

Now, I've seen it said that this is a bad idea, but without explanation for why. Since most results when googling for variants of "python import __main__" are questions about what if __name__ == '__main__' is, it's hard to find some solid information on this topic. So far I've had no problems with it, and it's actually been really convenient.

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

推荐答案

我认为有两个主要原因(哈哈)人们可能会建议避免这种模式.

I think there are two main (ha ha) reasons one might prescribe an avoidance of this pattern.

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

如果您完全控制了应用程序并且您的功能永远不会有另一个入口点或其他用途,并且您确定不介意歧义,我认为没有任何目标 from __main__ import foo 模式不好的原因.我个人不喜欢它,但再说一次,基本上是以上两个原因.

If you have total control over the application and there will never be another entry point or another use for your features, and you're sure you don't mind the ambiguity, I don't think there's any objective reason why the from __main__ import foo pattern is bad. I don't like it personally, but again, it's basically for the two reasons above.

我认为更健壮/对开发人员友好的解决方案可能是这样的,创建一个专门用于保存这些超级全局变量的特殊模块.然后,您可以导入模块并在需要设置时随时参考 module.VAR.本质上,只是创建一个特殊的模块命名空间来存储超级全局运行时配置.

I think a more robust/developer-friendly solution may be something like this, creating a special module specifically for holding these super-global variables. You can then import the module and refer to module.VAR anytime you need the setting. Essentially, just creating a special module namespace in which to store super-global runtime configuration.

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

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

You would then use it more like this:

# 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.这种结构意味着您可以在程序的生命周期中更改变量,并在其他地方反映该更改(显然,假设是单个线程/进程).

Note the use of conf.DEBUG rather than from conf import DEBUG. This construction means that you can alter the variable during the life of the program, and have that change reflected elsewhere (assuming a single thread/process, obviously).

另一个好处是这是一种相当常见的模式,因此其他开发人员很容易识别它.它很容易与各种流行应用程序(例如 django)使用的 settings.py 文件相提并论,尽管我避免使用该特定名称,因为 settings.py通常是一堆静态对象,而不是运行时参数的命名空间.例如,上述配置命名空间模块的其他好名称可能是 runtimeparams.

Another upside is that this is a fairly common pattern, so other developers will readily recognize it. It's easily comparable to the settings.py file used by various popular apps (e.g. django), though I avoided that particular name because settings.py is conventionally a bunch of static objects, not a namespace for runtime parameters. Other good names for the configuration namespace module described above might be runtime or params, for example.

这篇关于使用 `import __main__` 是好习惯吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆