作用域
1.代码中变量名被赋值的位置决定了这个变量名的作用域(即可见范围)。因为变量是在首次赋值的时候定义的。
- Python将一个变量名首次被赋值的地点关联为一个特定的命名空间
2.变量可以在3个不同的地方定义,对应三种不同的作用域:
def
内赋值定义的变量:作用域为本函数这里的赋值包括显式
=
赋值和隐式赋值。隐式赋值包括import
语句隐式赋值def
函数定义来隐式赋值(函数名这个变量)- 形参匹配来隐式赋值
- 嵌套的
def
中赋值定义的变量:对于父函数来说,该变量不是本地的 def
之外赋值,作用域为整个文件全局的
3.作用域法则:
- 每个模块文件就是一个全局作用域。从外面看,模块的全局变量就成为该模块对象的属性;从内部看,模块的全局变量就是普通的、作用域为全局的变量
- 全局作用域的范围仅限于变量所在的单个文件
- 每次调用函数,都创建一个新的局部作用域
- 默认情况下,所有函数定义内部的变量都是局部变量
global
语句声明会将变量名作用域提升至全局nonlocal
语句声明会将变量名作用域提升至外层的def
- Python预定义的
_builtin_
模块提供了一些预定义的内置变量
4.交互式运行的代码实际上位于__main__
的内置模块中
5.变量名查找规则:LGBE
- 首先查找本地作用域
L
- 接着查找上一层
def
或lambda
的本地作用域E
- 接着查找全局作用域
G
- 最后查找内置作用域
B
如果均未找到变量名则报错。
当前面作用域的变量名覆盖内置的作用域名字时,可以手动import builtins
模块,在用builtins.name
来直接使用这个变量名。注意不要随意修改builtins
模块内变量的值。
a=1
b=2
def func():
global a,b,c #一个名字或逗号分隔的多个名字,这些变量名被提升到全局作用域内
a=2
b=0
c=0 #虽然c没有在def外定义,但这里的global 和 c=0会在全局作用域内定义c
7.要在局部作用域中修改全局变量,方法有以下几种:
- global声明
- 通过
import modname
然后利用modname.attr
来访问 - 通过
import sys
然后利用sys.modules['modname'].attr
来访问
8.作用域示例:
x='global_x' #全局作用域
def f1():
x='f1_x' #f1的局部作用域
z='f1_z' #f1的局部作用域
def f2():
global y #全局作用域
y='f2_y'
print('in f2',x) #LGBE法则,找到的是f1局部作用域中的x
nonlocal z #f2的nonlocal作用域,是f1的局部作用域
z='f2_z'
t='f2_t' #f2的本地作用域
f2()
9.嵌套的函数返回后也是有效的。
def f1():
x=99
def f2():
print(x)
return f2 # f2是个局部变量,仅在f1执行期间因为新建了局部作用域所以才存在。
action=f1() #f2指向的函数对象,由于action引用它,因此不会被收回。
#但是f2这个位于局部作用域中的变量名被销毁了
action() #此时f1执行结束,但是f2记住了在f1嵌套作用域中的x。这种行为叫闭包
类比闭包在语义上更明确,因为类可以明确定义自己的状态
10.在函数内部调用一个未定义的函数是可行的,只要在函数调用是,该未定义函数已定义即可。
11.嵌套作用域中的变量在嵌套的函数调用时才进行查找,而不是定义时。 、
def func():
acts=[]
for i in range(5):
acts.append(lambda x:i**x) #添加匿名函数对象
return acts
acts=func()
acts[0](2) #调用时才开始查找i,此时i最后被记住的值是4
要解决这个这个陷阱,可以用默认参数:
def func():
acts=[]
for i in range(5):
acts.append(lambda x,i=i:i**x) #每次循环,形参i的默认实参均不同
return acts
acts=func()
acts[0](2)
12.nonlocal
是Python3中引入的。
def func():
a=0
b=1
def func1():
nonlocal a,b#限定了查找只能在func1所在的作用域(即func的本地作用域),且要求名字a,b已经存在。
a='a' #对a,b的赋值会影响到func中的a,b
- 如果没有
nonlocal
,则在func1
中的赋值会创建本地变量,而无法修改func
中的局部变量 global
使得作用域查找先从全局作用域开始,跳过了局部作用域以及nonlocal
作用域。- Python在函数创建的时候解析
nonlocal
,而不是在函数调用时,因此要求nonlocal
的名字已经存在
13.全局变量、nonlocal
变量、类、函数属性都提供了状态保持能力
14.global
、nonlocal
只是修改了变量作用域(即作用域提升),但是并未给出变量定义