为什么全局变量是邪恶的?

我试图找出为什么使用 global在 python 中被认为是不好的做法(在编程中也是如此)。有人能解释一下吗?链接与更多的信息也将受到欢迎。

115792 次浏览

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

然而,全局常量 在概念上与 全局变量不同; 全局常量是完全无害的。在 Python 中,这两者之间的区别纯粹是按照惯例进行的: CONSTANTS_ARE_CAPITALIZEDglobals_are_not

全局变量之所以糟糕,是因为它们使函数具有隐藏的副作用(不明显、令人惊讶、难以检测、难以诊断) ,从而导致复杂性增加,可能导致 意大利面密码

然而,即使在函数式编程中,全局状态的合理使用也是可以接受的(局部状态和可变性也是如此) ,无论是对于算法优化,降低复杂度,缓存和制表,还是对于源自主要命令式代码库的移植结构的实用性。

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

如果你想更深入地了解为什么会有副作用,以及其他许多有启发性的东西,你应该学习函数式编程:

是的,理论上是,全局变量(和一般的“状态”)是邪恶的。实际上,如果查看 python 的包目录,您会发现大多数模块都是从一系列全局声明开始的。显然,人们对它们没有意见。

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

在我的实践中,如果我碰巧在函数中修改了一个全局变量,我总是用 global声明它,即使在技术上没有这个必要,比如:

cache = {}


def foo(args):
global cache


cache[args] = ...

这使得追踪全球操纵变得更加容易。

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

此外,如果你认为其他人正在阅读你的代码(开源社区,同事等) ,他们将很难理解全局变量在哪里设置,在哪里被修改,以及从这个全局变量中期望什么,而不是一个孤立的函数,它的功能可以通过阅读函数定义本身来确定。

(可能)违反纯函数定义

我相信一个干净的(几乎)没有错误的代码应该具有尽可能纯的函数(参见 纯函数)。纯函数具有以下条件:

  1. 函数 总是计算给定相同参数值的相同结果值。函数结果值不能依赖于任何隐藏的信息或状态,这些信息或状态在程序执行过程中或程序不同执行之间可能会发生变化,它也不能依赖于来自 I/O 设备的任何外部输入(通常见下文)。
  2. 评估结果 不会引起任何语义上可观察到的副作用或输出,例如可变对象的突变或输出到 I/O 设备。

使用全局变量至少违反了上述规定之一(如果不是同时作为外部代码的话) ,可能会导致意外的结果。

纯函数的另一个明确定义是: “纯函数是一个接受 它所有的输入都是显式的参数并生成 它的所有输出都是显式的结果的函数。”[1].使用全局变量违反了纯函数的概念,因为输入和可能其中一个输出(全局变量)没有被显式地给出或返回。

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

此外,如果你考虑单元测试和 F.I.R.S.T 原则(Fast 测试,独立测试,Repeatable,是的self-Validating 和 Timely)可能会违反独立测试原则(这意味着测试不相互依赖)。

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

作为常量的全局变量

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

我不认为全局变量不应该被使用。但是,如果使用这些原则,作者应该考虑一些原则(上面提到的原则,以及其他软件工程原则和良好实践) ,以获得更清晰、几乎没有 bug 的代码。

它们是必不可少的,屏幕就是一个很好的例子。然而,在多线程环境中,或者在涉及到许多开发人员的情况下,实际上经常会出现这样的问题: 是谁(错误地)设置或清除了它?根据架构的不同,分析成本可能很高,并且经常需要分析。虽然可以读取全局变量,但是必须控制对它的写操作,例如通过单个线程或线程安全类。因此,全球变量产生了对高开发成本的恐惧,因为这些成本本身被认为是罪恶的。因此,总的来说,保持较低的全局变量数量是一个很好的实践。