Python 使用 `import __main__` 是好习惯吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24023601/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 03:50:46  来源:igfitidea点击:

Is it good practice to use `import __main__`?

pythonpython-2.7

提问by bheklilr

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 DEBUGflag that gets set by a command line option so that I can run my application in a debug mode without needing the full environment.

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

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.

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

Now, I've seen it saidthat 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 import __main__”的变体时的大多数结果都是关于什么的问题if __name__ == '__main__',因此很难找到关于这个主题的可靠信息。到目前为止,我没有遇到任何问题,而且它实际上非常方便。

So is this considered good Python practice, or is there a reason I should avoid this design?

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

采纳答案by Henry Keiter

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

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

  • It obfuscates the origin of the variables you're importing.
  • It breaks (or at least it's tough to maintain) if your program has multiple entry points. Imagine if someone, very possibly you, wanted to extract some subset of your functionality into a standalone library--they'd have to delete or redefine every one of those orphaned references to make the thing usable outside of your application.
  • 它混淆了您正在导入的变量的来源。
  • 如果您的程序有多个入口点,它就会中断(或者至少很难维护)。想象一下,如果有人,很可能是您,想要将您的功能的某个子集提取到一个独立的库中——他们必须删除或重新定义这些孤立的引用中的每一个,以使这些东西在您的应用程序之外可用。

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 objectivereason why the from __main__ import foopattern is bad. I don't like it personally, but again, it's basically for the two reasons above.

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



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.VARanytime you need the setting. Essentially, just creating a special module namespace in which to store super-global runtime configuration.

我认为更健壮/开发人员友好的解决方案可能是这样的,创建一个专门用于保存这些超级全局变量的特殊模块。然后您可以导入模块并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.

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')

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

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



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

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

回答by chepner

Doing so requires violating PEP8, which specifies

这样做需要违反PEP8,它指定

Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.

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

In order for gui.pyto successfully import __main__.DEBUG, you would have to set the value of DEBUGbeforeimport gui.

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