当前位置: 首页 > 面试题库 >

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

仇高韵
2023-03-14
问题内容

我正在开发一个相对较大的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 ...

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

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

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


问题答案:

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

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

如果您完全控制该应用程序,并且永远不会再有其他入口点或功能的其他用途,并且您确定自己不介意模棱两可,那么我认为模式错误是没有 客观 原因的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



 类似资料:
  • 问题内容: 关于Javadoc的内容不多。(简而言之:它返回字符串的规范表示形式,从而允许使用来比较内部字符串==) 我什么时候可以使用此功能? 是否存在Javadoc中未提及的副作用,即JIT编译器或多或少的优化? 还有其他用途吗? 问题答案: 我何时会使用此函数来支持String.equals() 当你需要速度时,因为可以按引用比较字符串(==比等于快) 是否有Javadoc中未提及的副作用?

  • 问题内容: 该方案。我在写与游戏相关的代码。在该游戏中,(同时也是一个类)具有的列表。有迹象表明,从继承其他类型的项目,例如,或。 显然,拥有我很方便。但是,当我获得玩家物品时,我唯一可以区分哪种物品的方法就是使用关键字。我确信我已经读过,依赖它是不好的做法。 在这种情况下可以使用吗?还是我应该重新考虑我的所有结构? 问题答案: 假设我正在写一些库存代码: 这样可以编译并正常工作。但是它错过了面向

  • 问题内容: 在企业应用程序中使用MS SQL Identity是否是好的做法?在创建业务逻辑以及将数据库从一个迁移到另一个时,难道不是很困难吗? 问题答案: 是的,它们工作得很好,可靠,性能最佳。与不使用身份字段相比,使用身份字段的一大好处是它们可以处理多个调用方尝试保留新ID的所有复杂的并发问题。这看起来似乎是微不足道的代码,但事实并非如此。 以下这些链接提供了一些有关身份字段以及为什么应尽可能

  • 问题内容: 将Assert用于函数参数以增强其有效性是否是一个好习惯。我浏览了Spring Framework的源代码,发现它们使用了很多代码。这是一个例子 这是另一个: 仅供参考,(不是语句)在util类中定义如下: 问题答案: 原则上,断言与许多其他运行时检查没有什么不同。 例如,Java在运行时对所有数组访问进行绑定检查。这会使事情变慢吗?是。有好处吗?绝对!一旦发生越界违规,就会引发异常,

  • 问题内容: 我有一个枚举: 使用方法检查枚举成员之间的“层次结构” 是否存在问题?我的意思是-使用它时,除了冗长之外,还有什么缺点吗?将来有人可能会意外更改顺序。 还是做这样的事更好: 问题答案: TLDR:不,您不应该! 如果您参考javadoc中的方法: 大多数程序员都不会使用这种方法。它设计用于复杂的基于枚举的数据结构,例如和。 首先-阅读手册(在这种情况下为javadoc)。 其次-不要编

  • 问题内容: 我发现了这种模式(或反模式),对此我感到非常满意。 我觉得它非常敏捷: 有时我用它的表弟: 我不需要创建人为的元组并计算参数并将%s匹配位置保留在元组中。 你喜欢它吗?您会/会使用它吗?是/否,请解释。 问题答案: 对于小型应用程序和所谓的“一次性”脚本,这是可以的,尤其是@ kaizer.se提到的增强功能和@RedGlyph提到的版本。 但是,对于维护寿命长且维护人员众多的大型应用