Python 中的「全局变量」的小细节

前几天被同学问了一个问题,为什么自己修改修改了全局变量但是没有生效,例子如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File: foo.py
l = 10

def bar():
global l
l = 20

#-----------------------------

# File: main.py
from foo import l, bar
if __name__ == '__main__':
print l
bar()
print l

输出:
10
10

所以我们还是要弄明白什么是全局变量。。。

Python 有全局变量么

有 - -|||。。。。。。
有两种情况是全局变量:

  • 在当前文件中的最外层作用于声明的变量为全局变量
  • 用 global 声明的变量为全局变量

Python 的「全局变量」的作用域是多大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# foo.py
l = 10


def bar():
global b
b = 1

--------------------------

# In ipython
In [1]: import foo

In [2]: 'l' in foo.__dict__
Out[2]: True

In [3]: 'b' in foo.__dict__
Out[3]: False

In [4]: foo.bar()

In [5]: 'b' in foo.__dict__
Out[5]: True

从上面的代码里我们得到以下几个结论

  • 全局变量的作用域被绑定到所在文件之下
  • 即使是先加载再声明全局变量,依然会绑定到该文件之中

为什么我不能跨文件修改全局变量

其实,正确的来说,这个问题问的并没有切中要点,在最初的代码中,我们无法修改 l 的主要原因是 import 的特性导致

The from form does not bind the module name: it goes through the list of identifiers, looks each one of them up in the module found in step (1), and binds the name in the local namespace to the object thus found.

所以当你执行这个语句

1
from foo import l

本质是在当前的 Namespace 中声明了变量 l, 并将 l 指向 foo 这个 module 中的 l 所指向的对象。

当你执行 foo.bar() 的时候,将 foo 中的 l 改为了 20, 但是 main.py 中 l 扔指向 10,所以并没有实现夸文件修改.
同理,在 main.py 中修改 l 也不会影响 foo.l

最后的最后

虽然前面举了例子,有分析了原理,但是其实就像是聊屠龙术的运行原理(更何况「全局变量」连屠龙书都算不上,顶多算是这个图里的 IE

除非是用来定义常量,否则不要在 Python 里用全局变量。