Python全局变量的疯狂

13 投票
4 回答
3714 浏览
提问于 2025-04-16 00:26

你有三个文件:main.py、second.py 和 common.py

common.py

#!/usr/bin/python
GLOBAL_ONE = "Frank"

main.py

#!/usr/bin/python
from common import *
from second import secondTest

if __name__ == "__main__":
    global GLOBAL_ONE
    print GLOBAL_ONE #Prints "Frank"
    GLOBAL_ONE = "Bob"
    print GLOBAL_ONE #Prints "Bob"

    secondTest()

    print GLOBAL_ONE #Prints "Bob"

second.py

#!/usr/bin/python
from common import *

def secondTest():
    global GLOBAL_ONE
    print GLOBAL_ONE #Prints "Frank"

为什么 secondTest 这个函数不使用它调用程序中的全局变量呢?如果它实际上不是全局的,那叫它“全局”有什么意义呢!?

我缺少什么才能让 secondTest(或者我从 main 调用的任何外部函数)识别并使用正确的变量呢?

4 个回答

2

对比一下下面的结果和你的结果。当你使用正确的命名空间时,你会得到你预期的结果。

common.py

#!/usr/bin/python
GLOBAL_ONE = "Frank"

main.py

#!/usr/bin/python
from second import secondTest
import common

if __name__ == "__main__":
    print common.GLOBAL_ONE # Prints "Frank"
    common.GLOBAL_ONE = "Bob"
    print common.GLOBAL_ONE # Prints "Bob"

    secondTest()

    print common.GLOBAL_ONE # Prints "Bob"

second.py

#!/usr/bin/python
import common

def secondTest():
    print common.GLOBAL_ONE # Prints "Bob"
5

首先,大家可能会问,为什么?

其实,全球变量在某些情况下是必要或有用的,但这种情况真的很少。

你的问题出在命名空间上。当你在第二个文件 second.py 中导入 common 时,GLOBAL_ONE 是来自那个命名空间的。当你导入 secondTest 时,它仍然引用的是来自 common.py 的 GLOBAL_ONE

不过,你真正的问题在于设计。我想不出有什么合理的 理由要这样使用全球变量。在 Python 中,全球变量是个棘手的事,因为没有常量变量的概念。不过,通常的做法是,如果你想在 Python 中保持某个值不变,你会把它命名为 WITH_ALL_CAPS。所以:

somevar = MY_GLOBAL_VAR  # good!
MY_GLOBAL_VAR = somevar  # What? You "can't" assign to a constant! Bad!

有很多理由说明这样做:

earth = 6e24
def badfunction():
    global earth
    earth += 1e5
print '%.2e' % earth

糟糕的。

当然,如果你只是想通过这个练习来理解命名空间和 global 的用法,那就继续吧。

如果不是,全球变量被认为是坏事的原因有:

  • 命名空间污染
  • 功能集成 - 你希望你的函数是独立的
  • 功能副作用 - 比如你写了一个函数去修改全球变量 balance,而你或者别人在使用这个函数时没有考虑到这一点,会发生什么?如果你在计算账户余额,突然之间你可能会多了或者少了。这种错误很难发现。

如果你有一个函数需要某个值,最好把这个值作为参数传递给它,除非你有很好的理由不这样做。比如说,全球变量 PI - 根据你的精度需求,你可能想要 3.14,或者想要 3.14159265……但这只是 一个 例子,说明全球变量有意义。实际上,能正确使用全球变量的情况可能只有一两种。比如在游戏编程中,常量的使用就很常见。导入 pygame.locals 用 KP_UP 比记住那个事件对应的整数值要简单得多。这些都是 例外

而且(至少在 pygame 中)这些常量是存储在一个单独的文件中 - 专门用来存放常量的。任何需要这些常量的模块都会导入这些常量。

在编程时,你会写函数来把问题分解成可管理的小块。理想情况下,一个函数应该只做 一件 事,并且没有副作用。这意味着像 calculatetime() 这样的函数应该只计算时间。它最好不要去读取包含时间的文件,更不能去做类似 时间的事情。它可以 返回 时间,如果需要参数也可以接收 - 这两者都是函数应该做的好事。函数就像是你(函数的编写者)和任何使用这个函数的人(包括你自己)之间的一个契约。访问 和修改 全球变量就违反了这个契约,因为函数可能会以未定义或未预期的方式修改外部数据。当我使用 calculatetime() 函数时,我期望它只计算时间并返回,而不是修改我刚导入的模块时间的全球变量。

修改全球变量会破坏契约,并模糊程序执行的逻辑区分。这可能会给你的程序带来错误,也会让升级和修改函数变得困难。当你把全球变量当作普通变量使用而不是常量时,死亡将以尖锐的牙齿等着你!

11

global的意思是对这个模块来说是全局的,而不是对整个程序都是全局的。当你执行

from lala import *

时,你把所有的lala的定义作为局部变量添加到这个模块中。

所以在你的情况下,你得到了两个GLOBAL_ONE的副本。

撰写回答