Python变量作用域

同样是网易一面的时候被问到的问题,而且感觉还是很基础的问题=。=

题目

1
2
3
4
5
6
7
8
9
10
def fs():
func_list = []

for i in range(5):
func_list.append(lambda : i)

return func_list

for f in fs():
print(f())

输出:

1
2
3
4
5
4
4
4
4
4

当时猜倒是猜对了,不过摆明了是猜的=。=

所以还是得好好学一下Python中变量作用域的只是。

作用域的产生

Python中变量的作用域与C有着很大的区别。在Python中,只有在模块(Module),函数(def)及类(class)中才有作用域的概念,而在if-else,for循环,while循环,try-except中则没有作用域的概念。

我们可以通过下面两个例子来理解这个概念。

1
2
3
4
def f():
i = 0
print(i)
print(i) # 上面函数中的i超出作用域而被回收

输出:

1
NameError: name 'i' is not defined

1
2
3
for i in range(5):
print(i)
print(i) # for循环没有作用域概念,因此这里的i指向同一个局部变量

输出:

1
2
3
4
5
6
0
1
2
3
4
4

作用域的类型

python的作用域分为四种:

  1. L(local)局部作用域
  2. E(enclosing)嵌套作用域:定义在此函数的上一层父级函数的局部作用域。
  3. G(global)全局作用域
  4. B(built-in)内置作用域:系统内固定模块里定义的变量,如预定义在builtin 模块内的变量。

变量名的解析

搜索变量名的优先级:局部作用域 > 嵌套作用域 > 全局作用域 > 内置作用域

LEGB法则: 当在函数中使用未确定的变量名时,Python会按照优先级依次搜索4个作用域,以此来确定该变量名的意义。首先搜索局部作用域(L),之后是上一层嵌套结构中def或lambda函数的嵌套作用域(E),之后是全局作用域(G),最后是内置作用域(B)。按这个查找原则,在第一处找到的地方停止。如果没有找到,则会出发NameError错误。

静态作用域

静态作用域是指声明的作用域是根据程序正文在编译时就确定的,有时也称为词法作用域。而在采用动态作用域的语言中,程序中某个变量所引用的对象是在程序运行时刻根据程序的控制流信息来确定的。

Python虽然属于解释性语言,但其变量作用域是在预编译阶段根据LEGB法则查找的。因此Python的作用域属于静态作用域。

参考

Python学习之变量的作用域
浅谈静态作用域和动态作用域