Python中的作用域、global与nonlocal
source link: https://note.qidong.name/2017/07/python-legb/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Python中的作用域、global与nonlocal
2017-07-08 23:02:23 +08 字数:1453 标签: Python
以下,以Python 3来介绍作用域相关的内容。
作用域 ¶
Python有四类作用域(Scope)。
- 局部(Local)作用域)
- 封闭(Enclosing)作用域
- 全局(Global)作用域
- 内置(Built-in)作用域
LEGB规则 ¶
在作用域中按名称去寻找对象(Python中一切皆对象)时,会按照LEGB规则去查找。 如果发生重名,也会按照LEGB规则,谁先被找到就用谁。
所谓LEGB规则,很简单,就是作用域查找顺序为:
Local -> Enclosing -> Global -> Built-in
以下,用示例代码来解释LEGB作用域,及其规则。
len = len([]) # (1)
def a():
len = 1 # (2)
def b():
len = 2 # (3)
print(len) # (4)
b()
a()
len
原本是Python的一个内置函数,在以上代码中被重新定义了三次。
在(1)中,右边的len([])
,就是内置的len
函数。
而左边的len =
,则是新定义的全局变量,值为0
。
在(2)中,又重新定义了len = 1
。
这里是函数a
的本地作用域,而对嵌套的函数b
来说,则是Enclosing作用域。
在(3)中,再次重新定义了len = 2
。
这是函数b
的本地作用域。
在(4)中,打印len
时,遵循LEGB规则,会打印Local的2
。
如果没有(3),会打印Enclosing,也就是函数a
里的1
。
如果没有(2),会打印Global里的0
。
如果没有(1),会打印<built-in function len>
。
Python没有块级作用域 ¶
Python里是没有块级作用域(Block scope)的。
for i in range(10):
pass
print(i)
以上代码会打印出9
。
这里是一个比较糟糕的设计。
如果有块级作用域,那么在for
块结束后,就不应该能访问i
。
而现在,会得到i
的最后一个值。
更糟糕的是,如果range()
里是0,那么for
循环会直接退出。
这时再print(i)
,结果就是:
NameError: name ‘i’ is not defined
所以,虽然Python没有块级作用域,但是建议就当它有。 不要在代码块以外,使用代码块内定义的东西。
关键字global ¶
前面只是谈及跨作用域的读取,还没谈及跨作用域的写入。
i = 0
def a():
i = 1
print('local:', i)
a()
print('global:', i)
以上代码的输出结果为:
local: 1
global: 0
也就是说,a
函数里的i = 1
,是定义一个新的局部变量,而非对全局变量的修改。
如果需要对全局变量i
进行修改,则需要使用Python的global
关键字。
i = 0
def a():
global i
i = 1
print('local:', i)
a()
print('global:', i)
以上代码的输出结果为:
local: 1
global: 1
关键字nonlocal ¶
修改Global变量的问题,可以用global
关键字来解决;
修改Enclosing变量的问题,就需要使用nonlocal
关键字。
i = 0
def a():
i = 1
def b():
nonlocal i
i = 2
b()
print(i)
a()
以上代码的输出结果为2
,这就是nonlocal
的功效。
nonlocal
不能代替global
。
如果在上述代码的函数a
中,就只能使用global
。
因为,外层就是Global作用域了。
在多重嵌套中,nonlocal
只会上溯一层;
而如果上一层没有,则会继续上溯。
def a():
i = 1
def b():
# i = 2
def c():
nonlocal i
i = 3
c()
print('b:', i)
b()
print('a:', i)
a()
以上代码的输出结果为:
b: 3
a: 3
因为函数b
中没有i
,所以nonlocal
上溯到了函数a
中。
(注意:如果a
中也没有,就回到了Global作用域,nonlocal
会报错。)
如果以上代码中,函数b
里的注释# i = 2
去掉,则输出变为:
b: 3
a: 1
因为nonlocal
只会上溯一层,到函数b
,所以函数a
中的i
没有被修改。
Python 2的问题 ¶
LEGB规则是在Python 2.2以后确立的,在那以前不成立,是另一套。
Python 2中没有nonlocal
关键字,所以无法对Enclosing作用域的上一层,进行写操作。
参考 ¶
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK