关于python:为什么全局变量是邪恶的?

Why are global variables evil?

我试图找到一个很好的源代码,解释为什么在Python(以及一般编程中)中使用global被认为是不好的做法。有人能给我指一个或解释一下吗?


这与Python无关;全局变量在任何编程语言中都是不好的。

但是,全局常量在概念上与全局变量不同;全局常量是完全无害的。只是在python中没有强制的差异,只有在约定中是CONSTANTS_CAPITALIZEDglobals_are_not

它们之所以不好,是因为它们允许函数具有隐藏(不明显、令人惊讶、难以检测)的副作用,从而导致复杂性增加,可能导致意粉代码。

然而,即使在函数式编程中,无论是为了优化算法还是降低复杂性,还是为了缓存和记忆化,以及为了从必要的代码库中移植逻辑的实用性,全局状态的正常使用也是可以接受的(局部状态和易变性也是可以接受的)。

总而言之,你的问题可以用很多方式来回答,所以你最好的选择就是谷歌"为什么全局变量是坏的"。一些例子:

  • http://c2.com/cgi/wiki?全球变量错误
  • https://softwarengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil
  • 全局变量是否坏?

如果你想更深入地了解副作用的原因,以及其他许多有启发性的事情,你应该学习函数式编程:

  • https://en.wikipedia.org/wiki/side-effect(计算机科学)
  • https://softwarengineering.stackexchange.com/questions/15269/why-are-side-effects-considered-evil-in-functional-programming
  • https://en.wikipedia.org/wiki/functional_编程

是的,理论上,全球(和一般的"国家")是邪恶的。实际上,如果您查看python的packages目录,您会发现那里的大多数模块都是从一系列全局声明开始的。显然,人们对他们没有问题。

特别是对于python,globals的可见性仅限于一个模块,因此不存在影响整个程序的"真"globals——这使得它们的危害性大大降低。另一点:没有const,所以当你需要一个常量时,你必须使用一个全局变量。

在我的实践中,如果我碰巧在函数中修改了一个全局,我总是用global声明它,即使技术上不需要这样做,如:

1
2
3
4
5
6
cache = {}

def foo(args):
    global cache

    cache[args] = ...

这使得全局操作更容易被跟踪。


个人对这个主题的看法是,在函数逻辑中使用全局变量意味着其他一些代码可以改变该函数的逻辑和预期输出,这将使调试非常困难(尤其是在大型项目中),也将使测试更加困难。

此外,如果您考虑到其他人阅读您的代码(开源社区、同事等),他们将很难理解全局变量的设置位置、更改位置以及对该全局变量的期望,而不是通过阅读fu确定其功能的独立函数。NCtion定义本身。

(可能)违反纯函数定义

我相信一个干净且(几乎)无缺陷的代码应该具有尽可能纯净的函数(参见pure函数)。纯函数是指具有以下条件的函数:

  • 函数总是在给定相同参数值的情况下计算相同的结果值。函数结果值不能依赖于在程序执行过程中或在程序的不同执行之间可能更改的任何隐藏信息或状态,也不能依赖于来自I/O设备的任何外部输入(通常见下文)。
  • 结果的评估不会导致任何语义上可观察到的副作用或输出,例如可变对象的突变或输出到I/O设备。
  • 如果全局变量不同时作为一个外部代码,则至少会违反上面的一个规则,这可能会导致意外的结果。

    对纯函数的另一个明确定义是:"纯函数是一个将其所有输入作为显式参数,并将其所有输出作为显式结果的函数。"[1]。拥有全局变量违反了纯函数的概念,因为输入和其中一个输出(全局变量)没有被显式地给出或返回。

    (可能)违反单元测试F.I.R.S.T原则

    此外,如果考虑单元测试和F.I.R.S.T原则(快速测试、独立测试、可重复性、自我验证和及时性),可能会违反独立测试原则(这意味着测试不相互依赖)。

    有一个全局变量(不总是)但在大多数情况下(至少我目前看到的情况下)是准备结果并将其传递给其他函数。这也违反了这一原则。如果全局变量以这种方式使用(即函数x中使用的全局变量必须首先在函数y中设置),则意味着要对函数x进行单元测试,必须首先运行测试/运行函数y。

    全局作为常量

    另一方面,正如其他人已经提到的那样,如果将全局变量用作"常量"变量,由于语言不支持常量,因此效果会稍好一些。但是,我总是喜欢使用类,并将"常量"作为类成员,而根本不使用全局变量。如果您有一个代码,两个不同的类需要共享一个全局变量,那么您可能需要重构解决方案并使类独立。

    我不认为不应该使用环球报。但是,如果使用它们,作者应该考虑一些原则(上面提到的那些可能和其他软件工程原则和良好实践)来获得更干净、几乎没有缺陷的代码。


    它们是必不可少的,屏幕就是一个很好的例子。然而,在多线程环境中,或者在涉及许多开发人员的情况下,在实践中经常会出现这样的问题:是谁(错误地)设置或清除了它?根据体系结构的不同,分析可能会很昂贵,并且经常需要。虽然读取全局变量可以是正常的,但是必须控制对它的写入,例如由单个线程或线程安全类控制。因此,全球var因其自身被视为邪恶的后果而产生对高开发成本的恐惧。因此,一般来说,保持较低的全局变量数是一种良好的做法。