【问题标题】:Python global keyword vs. Pylint W0603Python 全局关键字与 Pylint W0603
【发布时间】:2013-01-24 14:44:36
【问题描述】:

Pylint W0603 状态:

使用全局语句。当你使用“全局”语句来 更新一个全局变量。 PyLint 只是试图阻止这种用法。 这并不意味着你不能使用它!

我想知道为什么会这样?有没有更多的 Pythonic 方式来修改函数内不可变的、模块范围的变量?是否建议将它们打包在字典等可变变量中?或者也许把整个模块变成类?

在我看来,当变量被建议为“私有”(以 _ 或 __ 为前缀)时,此警告应该消失。

【问题讨论】:

  • “有没有更多的 Pythonic 方式来修改不可变的、模块范围的变量”这是 PyLint 令人沮丧的事情。
  • 为什么这是个坏主意?假设我需要从 o 模块中的不同函数写入单个文件。共享此文件名称的最佳解决方案是什么?

标签: python global-variables global pylint


【解决方案1】:

全局变量的普遍使用会使维护成为一场噩梦,因为它们会跟踪程序的流程,有时您会遇到奇怪的错误,因为某些模块在其他模块更改值之前已经读取了变量并对其值采取了行动变量(这可能是由于在一些不相关的第三个模块中反转了两个导入语句)。另见the wikipedia entry on Global variables

这就是为什么您应该避免可变全局变量、IMO 以及 Pylint 发出警告的原因(并且可能应该发出更多警告。检测 global 关键字的使用只是发现其中一些的简单方法) .

不要误会我的意思:我并不是说你不能使用全局变量。只是你应该避免使用它们。 Python中有很多全局变量的合法案例。只要你没有得到超过几个 W0603,你应该可以接受。

现在,Logilab(维护 Pylint 的公司,也是我曾经工作的地方)曾经不得不接管超过 50kloc 的 Python 代码的维护工作,其中包含大量重复和 100 多个可变全局变量。这是地狱。

解决全局变量的解决方案包括:

  • 为需要访问变量的函数添加参数
  • 使用类属性
  • 使用实例属性(通过将您需要的值传递给类的构造函数)
  • 将可变的全局值集中在一个配置对象中,该对象被构建以便在程序启动时实例化一次(使用环境变量、命令行、配置文件...),并且在此之后永远不会发生变异。

【讨论】:

    【解决方案2】:

    我会替换这个:

    the_file = None
    
    def open_the_file(fname):
        global the_file
        the_file = open(fname)
    
    def write_to_the_file(data):
        the_file.write(data)
    
    open_the_file("boo")
    write_to_the_file("Hi!")
    

    用这个:

    class FileProgram(object):
        def __init__(self):
            self.the_file = None
    
        def open_the_file(fname):
            self.the_file = open(fname)
    
        def write_to_the_file(data):
            self.the_file.write(data)
    
    if __name__ == "__main__":
        prog = FileProgram()
        prog.open_the_file("boo")
        prog.write_to_the_file("Hi!")
    

    您可能会说,“这对于我的简单任务来说太复杂了!”好的,那么不要在你的程序上运行 pylint 。您不能要求 pylint 了解您的程序太小而无法使用良好的结构。

    【讨论】:

    • 你不应该在某处有def close_the_file吗?或者更好的是,将您的班级变成上下文管理器!哇!
    【解决方案3】:

    在 python 中,像这样共享全局数据的模块几乎是单例的。你可以用singleton 类完成同样的事情,或者问自己是否有任何理由你真的需要一个单身人士。如果您不需要需要单例——使用常规类(实例)。如果你确实需要一个单例,谷歌/搜索 SO 来找出你认为最适合你的模式。也许你确实想要一个模块——global 有一个小众市场——否则 Guido 很久以前就会把它从语言中删掉——这个小众市场恰好很小......

    【讨论】:

    • 这似乎是一个很大的开销,例如在模块内的函数之间共享一个文件名。看到 Pylint W0603 警告我很惊讶,因为我从来没有遇到过这种对global 声明的负面态度
    • @Jovik - 那么您还没有阅读很多 OP 使用 global 的 SO 问题;-)。无论如何,Pylint 只是一个根据 Pylint 开发人员的理想代码检查代码的项目。其中一些得到了很好的支持(例如 PEP 8 合规性),但是您可以随意编写代码(即使 Pylint 抱怨)。我只是列出了一些可能会让 Pylint 高兴的替代方案。
    • 我喜欢让我的代码符合 Pylint;希望其他人更容易理解。通常我将全局变量保存在字典中,这使得global 无用,但这次我需要保留对单个对象的引用。感谢您的宝贵时间。
    • 全局状态数据通常被认为是一个糟糕的设计选择,请参阅:为什么全局状态如此邪恶? programmers.stackexchange.com/questions/148108/…
    • @Jovik:将全局变量放入 dict 以避免 pylint 警告完全没有抓住重点。关键是要避免全局状态。将其隐藏在字典中以避免触发警告的语句只是将黑色胶带放在发动机警告灯上。解决不了问题。
    【解决方案4】:

    同意将其重构为类是方法。有一些特殊情况,类不是最优的,那么我推荐使用dataclass

    from dataclasses import dataclass
    
    @dataclass
    class Files:
        the_file = "fname"
    
    files = Files()
    
    def open_the_file():
        files.the_file = "123"  # You can edit it here
    

    这有一个很大的好处,如果你从其他模块导入它,它不是复制,而是引用,所以如果你在某个地方更改值,你仍然可以在任何地方得到正确的值。

    但是...通常上课是这样。什么时候用这种方式?如果我希望只存在一个实例(一种单例),我个人会使用它。或者,如果在公共 api (_init_.py) 中,我只想使用该函数来消除用户的复杂性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多