变量作用域规则

全局变量

定义一个函数,它读取两个变量的值:一个是局部变量a,是函数的参数,另一个是变量b,这个函数没有定义它

def f1(a):
    print(a)
    print(b)

print(f1(3))


# 运行结果
3
Traceback (most recent call last):
  File "E:/virtual_workshop/everyday/test/f1.py", line 6, in <module>
    print(f1(3))
  File "E:/virtual_workshop/everyday/test/f1.py", line 3, in f1
    print(b)
NameError: name 'b' is not defined

出现这样的错误并不奇怪,因为全局变量b并没有赋值,使用dis反汇编一下,可以看到是往栈里load了全局变量b

from dis import dis

print(dis(f1))


# 运行结果
 2           0 LOAD_GLOBAL              0 (print)
              2 LOAD_FAST                0 (a)
              4 CALL_FUNCTION            1
              6 POP_TOP

  3           8 LOAD_GLOBAL              0 (print)
             10 LOAD_GLOBAL              1 (b)   #往栈里load了全局变量b
             12 CALL_FUNCTION            1
             14 POP_TOP
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
None

怎么优化这个程序?只需要先给全局变量b赋值,然后再调用f

def f1(a):
    print(a)
    print(b)

b = 6
print(f1(3))


# 运行结果
3
6
None

局部变量

下面的f2函数,先给b赋值,然后再打印a和b的值

b = 6

def f2(a):
    print(a)
    print(b)
    b = 9   #局部变量

print(f2(3))


# 运行结果
3
Traceback (most recent call last):
  File "E:/virtual_workshop/everyday/test/f2.py", line 9, in <module>
    print(f2(3))
  File "E:/virtual_workshop/everyday/test/f2.py", line 6, in f2
    print(b)
UnboundLocalError: local variable 'b' referenced before assignment

为什么会出现这样的情况呢,因为Python编译函数的时候,它判定b是局部变量,因为在函数中给它赋值了。生成的字节码正事了这种判断,Python会尝试从本地环境获取b。后面调用f2(3)时,f2的定义体会获取并打印局部变量a的值,但是尝试获取局部变量b的值时,发现b没有绑定值。这不是缺陷,而是设计选择:Python不要求声明变量,但是假定在函数体中赋值的变量是局部变量。使用dis反汇编一下,可以看到加载的b是局部变量

  5           0 LOAD_GLOBAL              0 (print)
              2 LOAD_FAST                0 (a)
              4 CALL_FUNCTION            1
              6 POP_TOP

  6           8 LOAD_GLOBAL              0 (print)
             10 LOAD_FAST                1 (b)   #往栈里load了局部变量b
             12 CALL_FUNCTION            1
             14 POP_TOP

  7          16 LOAD_CONST               1 (9)
             18 STORE_FAST               1 (b)
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE
None

如果在函数中赋值时想让解释器把b当做全局变量,需要用global声明:

b = 6

def f2(a):
    global b
    print(a)
    print(b)
    b = 9   #局部变量


# 运行结果
3
6
None

参考书籍

《流畅的Python》

原文地址:https://www.cnblogs.com/my_captain/p/12843358.html