# 作用域
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`只是修改了变量作用域(即作用域提升),但是并未给出变量定义
