# global和nonlocal有什么区别?
面试官问:"global和nonlocal分别用来做什么?什么时候必须用?"
很多人知道"global修改全局变量",但不清楚只读全局变量是否需要声明、nonlocal查找哪一层作用域,以及两者不能混用的原因。
# 简要回答
global声明名字引用全局作用域的变量,让函数内的赋值操作修改模块级全局变量,而不是创建局部变量。
nonlocal声明名字引用外层函数的局部变量,让内层函数可以修改外层函数(Enclosing作用域)的变量。
只读变量不需要声明,只有赋值操作才需要global或nonlocal。global作用于模块全局作用域,nonlocal作用于最近的外层函数作用域,两者不能同时声明同一个名字。
# 详细回答
# 只读全局变量不需要声明
函数内可以直接读取全局变量:
count = 0
def read_count():
print(count) # 读取全局变量,不需要global
read_count() # 0
2
3
4
5
6
LEGB规则自动查找到Global作用域的count。
# 修改全局变量需要global
如果函数内对全局变量赋值,必须用global声明:
count = 0
def increment():
global count
count = count + 1 # 修改全局变量
increment()
print(count) # 1
2
3
4
5
6
7
8
不声明会报错:
count = 0
def increment():
count = count + 1 # UnboundLocalError
increment()
2
3
4
5
6
因为赋值操作让Python认为count是局部变量,但在赋值前读取count时,局部变量还没有值。
# global的作用范围
global声明后,整个函数内对该名字的赋值都作用于全局作用域:
x = 10
def func():
global x
x = 20
x = x + 5
print(x) # 25
func()
print(x) # 25,全局x被修改
2
3
4
5
6
7
8
9
10
# nonlocal修改外层函数变量
嵌套函数可以读取外层函数的局部变量,但修改时需要nonlocal:
def outer():
count = 0
def inner():
nonlocal count
count = count + 1
inner()
print(count) # 1
outer()
2
3
4
5
6
7
8
9
10
11
不声明会创建局部变量,外层的count不变:
def outer():
count = 0
def inner():
count = count + 1 # UnboundLocalError
inner()
outer()
2
3
4
5
6
7
8
9
# nonlocal查找最近的外层作用域
nonlocal查找最近的Enclosing作用域,不会跨越到Global:
def outer():
x = "outer"
def middle():
x = "middle"
def inner():
nonlocal x
x = "inner"
inner()
print(x) # inner,修改的是middle的x
middle()
print(x) # outer,outer的x未被修改
outer()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
inner中的nonlocal x绑定到middle的局部变量x,不会绑定到outer的x。

# nonlocal必须找到外层变量
nonlocal声明的名字必须在外层作用域已经存在,否则报错:
def outer():
def inner():
nonlocal count # SyntaxError: no binding for nonlocal 'count' found
count = 0
inner()
outer()
2
3
4
5
6
7
8
global可以创建全局变量,nonlocal不能创建外层变量。
# global和nonlocal不能同时声明
一个名字不能同时用global和nonlocal声明:
x = 10
def outer():
x = 20
def inner():
global x
nonlocal x # SyntaxError: name 'x' is nonlocal and global
x = 30
inner()
outer()
2
3
4
5
6
7
8
9
10
11
12
13
必须选择其中一个。
# 闭包中的修改
nonlocal是实现闭包计数器的关键:
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c1 = make_counter()
print(c1()) # 1
print(c1()) # 2
c2 = make_counter()
print(c2()) # 1,独立的计数器
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
每个闭包保存独立的count,nonlocal让counter可以修改这个外层变量。
# 知识拓展
Q1:可以用global声明不存在的全局变量吗?
可以。global声明后,函数内的赋值会在全局作用域创建变量:
def create_global():
global new_var
new_var = 100
create_global()
print(new_var) # 100
2
3
4
5
6
但这样的代码可读性差,容易引入隐式依赖。
Q2:为什么只读变量不需要声明?
因为LEGB规则自动查找。只有赋值操作会创建新的局部变量,破坏查找链,所以需要显式声明"这个名字不是局部的"。
Q3:可以在一个global语句中声明多个变量吗?
可以:
x, y = 10, 20
def func():
global x, y
x = 30
y = 40
func()
print(x, y) # 30 40
2
3
4
5
6
7
8
9
nonlocal同理。
Q4:在类方法中可以用global修改类属性吗?
不能。global只作用于模块全局作用域,不作用于类命名空间:
class MyClass:
x = 10
def method(self):
global x # 声明模块全局的x,不是类属性
x = 20
obj = MyClass()
obj.method()
print(MyClass.x) # 10,类属性未变
print(x) # 20,创建了模块全局变量
2
3
4
5
6
7
8
9
10
11
修改类属性用MyClass.x = 20或self.__class__.x = 20。
Q5:什么时候应该避免用global和nonlocal?
频繁使用global会引入全局状态,降低函数的可测试性和可维护性。更好的做法:
- 用参数传递需要修改的值。
- 用类封装状态。
- 返回新值而不是修改外部变量。
nonlocal在闭包中合理使用,但嵌套层数过多时会降低可读性。
评论
验证登录状态...