作用域
作用域就是作用范围,python的作用域是静态的,在代码中变量名被赋值的位置决定了该变量能被访问的范围。即:Python变量的作用域由变量所在源代码中的位置决定。
- 作用域的分类
python中的作用域分4种情况:
L:local,局部作用域,即函数中定义的变量;
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
G:global,全局变量,就是模块级别定义的变量;
B:built-in,系统固定模块里面的变量,比如int, bytearray等。
python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量。即常说的 “LEGB原则”。
- 作用域的产生
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if-else、try-except、for等)是不会引入新的作用域的,如下代码【注:以下代码仅作讲解使用】:
if __name__ == "__main__":
# if-else
if True:
x = 1
else:
x = 0
print(x)
'''
1
'''
# for
for i in range(10):
i += 1
print(i)
'''
10
'''
# 函数
def func():
y = 1 # 局部变量,仅在函数中生效
func()
print(y)
'''
程序报错:NameError: name 'y' is not defined
'''
- 局部变量的修改
Python中不在局部作用域里的变量默认是只读的,如果试图为其绑定一个新的值,Python认为是在当前的局部作用域里创建一个新的变量。如下代码:
a = 1
def func2():
print(a)
a = 2
print(a)
func2()
'''
程序报错:UnboundLocalError: local variable 'a' referenced before assignment
'''
在第一个print中,使用到了外部作用域a,这样Python会认为后面出现的a就是外部作用域中的a了,再修改就会报错。 如果没使用过这个变量,而直接赋值,会认为是新定义的变量,此时会覆盖外部作用域中变量,如下代码:
a = 1
def func3():
a = 2
print(a)
func3()
'''
2
''''
global关键字
- 声明一个全局变量。
- 在局部作用域想要对全局作用域的全局变量【不可变类型】进行修改时,需要用到 global。
- 对可变数据类型(list,dict,set)可以直接引用而不用通过global。
count = 1
def search():
global count
count = 2
search()
print(count)
'''
2
'''
A = [1,2,3]
def change():
A.append('a')
print(A)
change()
print(A)
'''
[1, 2, 3, 'a']
[1, 2, 3, 'a']
'''
nonlocal关键字
- 不能修改全局变量。
- 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
注:nonlocal关键字是在Python3之后才有的,优化了Python编程中的闭包问题。
def add_b():
b = 42
def do_global():
b = 10
print(b)
def do_nonlocal():
nonlocal b
b = b + 20
print(b)
do_nonlocal()
print(b)
do_global()
print(b)
add_b()
'''
10
30
30
42
'''
综合实例练习:
def scope_test():#定义函数
def do_local():#嵌套函数
spam = "local spam"#函数里面定义给变量spam赋值
def do_nonlocal():#嵌套函数
nonlocal spam #嵌套函数内部定义变量spam的作用域
spam = "nonlocal spam"# 定义变量spam
def do_global():#嵌套函数
global spam #在嵌套函数内部定义变量spam作用域
spam = "global spam"#定义变量spam
spam = "test spam"
do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:",spam)
do_global()
print("After global assignment:",spam)
scope_test()
print("In global scope:", spam)
'''
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
'''