全局变量与导入模块

34

first.py

myGlobal = "hello"

def changeGlobal():
   myGlobal="bye"

second.py

from first import *

changeGlobal()
print myGlobal

我得到的输出是

hello

尽管我认为它应该是

  

bye

为什么在调用changeGlobal()函数后全局变量myGlobal没有改变?

3个回答

21

尝试:

def changeGlobal():
    global myGlobal
    myGlobal = "bye"

实际上,那也不起作用。当你使用import *时,你会创建一个新的全局模块myGlobal,它对于你打算进行的更改是免疫的(只要你不改变变量,见下文)。你可以使用以下代码替代:

import nice

nice.changeGlobal()
print nice.myGlobal

或者:

myGlobal = "hello"

def changeGlobal():
   global myGlobal
   myGlobal="bye"

changeGlobal()

然而,如果你的全局变量是可变容器,那么你现在持有一个对可变容器的引用,并且能够看到对它所做的更改:

myGlobal = ["hello"]

def changeGlobal():
    myGlobal[0] = "bye"

1
我在原始帖子中忘记提到我尝试使用全局变量,但它没有起作用。我不知道当使用*导入时会创建一个新的本地变量。谢谢! - Joel
6
说你创建了一个新的本地变量并不完全正确,实际上是在第二个模块中创建了一个新的全局变量。每个模块都有自己的一组全局变量。 - Brian Goldman
“import nice”是最好的选择,但是如何像“import app.base.admin.crud as cx”一样导入绝对路径的文件夹作为包呢?请参考https://stackoverflow.com/questions/48098231/python-how-do-i-import-folder-with-absolute-path-as-package。 - TomSawyer

8
我曾经有和你一样的疑虑,阅读 Norman MatloffQuick and Painless Python Tutorial 中以下章节真的很有帮助。这是你需要理解的(摘自Matloff的书):
Python不像C/C++那样真正允许全局变量。导入的Python模块将无法直接访问导入它的模块的全局变量,反之亦然。
例如,考虑这两个文件,x.py
# x.py
import y
def f():
  global x
  x = 6
def main():
  global x
  x = 3
f()
y.g()
if __name__ == ’__main__’:
  main()

以及 y.py:

# y.py
def g():
  global x
  x += 1

x.py 文件中,变量 x 在整个模块 x.py 中都可以被访问,但在 y.py 中却不行。实际上,在后者执行以下代码时: x += 1 会导致出现错误信息:"global name 'x' is not defined"。
事实上,模块中的全局变量只是该模块的属性(即成员实体),类变量在类内的作用也与此类似。当模块 B 被模块 A 导入时,B 的命名空间被复制到 A。如果模块 B 有一个全局变量 X,则模块 A 将创建一个同名变量,其初始值为模块 B 在导入时对该变量的值。但是,在一个模块中更改 X 不会反映在另一个模块中。
假设 X 在 B 中发生了更改,但我们希望在 A 中的代码能够获取 B 中 X 的最新值。我们可以在 B 中包含一个函数,称为 GetX()。假设 AB 中导入了所有内容,则 A 将得到一个名为 GetX() 的函数,它是 B 中同名函数的副本,其唯一目的是返回 X 的值。除非 B 更改该函数(例如函数可能被赋值),否则两个模块中的函数始终是相同的,因此 A 可以使用它的函数来获取 B 中 X 的值。

1
谢谢,Python全局变量并不是真正的全局变量,它们只是模块属性——很多人都会陷入这个“bug”中。现在,如果你使用了'from foo import *',那么是否有一个名为'foo'的模块可能拥有来自foo的“全局变量”?还是说foo中的所有内容现在都被合并到“当前模块”中了? - Ribo
我的测试与您的陈述相矛盾,即“导入模块B时”...该模块B的名称空间被复制到当前名称空间(即模块A的名称空间)。当我从命令行调用Python(即不在模块A中)并导入一个模块时,导入模块的“全局变量”不会出现(被复制)到当前名称空间。导入模块的名称在名称空间(globals())和moduleName.itsGlobalVar中定义,但其GlobalVar不在globals()中。 - Ribo

2

Python全局变量并不是真正的全局

正如wassimans在上面指出的,它们实际上是定义它们的模块的作用域内的属性(或包含定义它们的函数的模块)。

人们遇到的第一个混淆(错误)是没有意识到函数有一个本地名称空间,并且在函数中设置变量会使它成为函数的本地变量,即使他们原本想要更改封闭模块中同名的(全局)变量。(在函数中声明该名称的"global"语句,或在设置之前访问(全局)变量。)

人们遇到的第二个混淆(错误)是每个模块(即导入的文件)都包含自己的所谓"全局"名称空间。我猜python认为世界(地球)就是这个模块--也许我们正在寻找跨越多个地球的"通用"变量。

第三个混淆(我现在开始理解的)是在__main__模块中的"全局"变量在哪里?也就是说,如果您从命令行以交互模式启动python,或者如果您调用python脚本(从命令行键入foo.py的名称)--没有导入可以使用名称的模块。

'globals()' 或者 globals().keys() 的内容,可以通过以下方式访问:dir(sys.modules['__main__'])。似乎加载的Python脚本的模块(或无加载脚本的交互会话)中,以__name__命名的模块没有全局名称,但可以作为系统所有活动模块列表sys.modules中名称为'__main__'的模块进行访问。

@Andrew,什么没有起作用?当你做了你所做的事情时,它做了什么? - Ribo
sys.modules['main'] - Andrew
1
哦!文本格式化程序将双下划线解释为“使其成为斜体”。我在__name__和__main__周围“转义”了双下划线,以便它们正确显示。此外,似乎需要使用dir()而不是.keys()。 - Ribo
太棒了,现在它可以工作了,这太完美了,谢谢!所有人只需要在导入的文件顶部添加例如:g = sys.modules['__main__'],然后他们就可以访问g.whatever - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接