目录

  • 作用域定义
  • 作用域分类
  • 查找变量规则

9)Python作用域

作用域定义

作用域 是命名空间可直接访问的 Python 程序的文本区域。 “可直接访问” 的意思是,对名称的非限定引用会在命名空间中查找名称。

作用域分类

作用域虽然是静态确定的,但会被动态使用。执行期间的任何时刻,都会有 3 或 4 个命名空间可被直接访问的嵌套作用域:

  • (Local) 局部作用域:最内层作用域,包含局部名称,并首先在其中进行搜索
  • (Enclosing) 闭包函数作用域:封闭函数的作用域,包含非局部名称和非全局名称,从最近的封闭作用域开始搜索
  • (Global) 全局作用域:倒数第二个作用域,包含当前模块的全局名称
  • (Built-in) 内置作用域:最外层的作用域,包含内置名称的命名空间,最后搜索
查找变量规则
  1. python 寻找变量时候会按照L-E-G-B的顺序去查找,如果找不到则报ValueError
  2. python变量作用域取决于其函数代码块在整体代码中的位置,而不是被调用的位置:
    示例如下:
1| a = 1 
 2| 
 3| def func2():
 4|     print(a)
 5| 
 6| def func1():
 7|     a = 10 
 8|     func2()
 9| 
10| func1()
11| >>> 1

func2() 的调用位置是第8行,但是a依然获取的是 第1行中的 “a = 1”

如果将fun2()写成闭包,则如下示例:

1| a = 1 
 2|
 3| def func1():
 4|     a = 10 
 5|     def func2():
 6|         print(a)
 7|     func2()
 8| 
 9| func1()
10| >>> 10
  1. 如果函数体再没有声明的情况下,直接对可以查找到的外部变量进行修改则会报错:UnboundLocalError
a = 1

def func1():
    a += 1
    print(a)
   
func1()

因为python规定,函数内部要修改一个变量,那么这个变量必须是内部变量,或者使用全局变量global进行声明!

a = 1

def func1():
    global a
    a += 1
    print(a)
   
func1()
  1. 全局变量 global & 非全局变量nonlocal
    示例1 演示如何引用不同作用域及命名空间
def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global 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

注意,局部 赋值(这是默认状态)不会改变 scope_testspam 的绑定。 nonlocal 赋值会改变 scope_testspam 的绑定,而 global 赋值会改变模块层级的绑定。

而且,global 赋值前没有 spam 的绑定。

如前所述,python寻找变量会按照 L - E - G - B的顺序进行查找,global的作用就是声明使用G层的变量。

想要使用E层的变量怎么办呢?可以使用nonlocal 进行声明,示例如下:

a = 1
print("函数outer调用之前全局变量a的内存地址: ",id(a))

def outer():
    a = 2 
    print("函数outer调用之时闭包外部的变量a的内存地址: ",id(a))
    def inner():
        nonlocal a
        a = 3
        print("函数inner调用之后闭包内部变量a的内存地址: ",id(a))
    inner()
    print("函数inner调用之后,闭包外部的变量a的内存地址: ",id(a))
outer()
print("函数outer执行完毕,全局变量a的内存地址: ",id(a))

>>>
函数outer调用之前全局变量a的内存地址:  2060964686128
函数outer调用之时闭包外部的变量a的内存地址:  2060964686160
函数inner调用之后闭包内部变量a的内存地址:  2060964686192
函数inner调用之后,闭包外部的变量a的内存地址:  2060964686192
函数outer执行完毕,全局变量a的内存地址:  2060964686128

总结起来即:global声明使用全局变量,nonlocal声明使用函数嵌套关系外层的变量,即闭包外部的变量。

global !global! 运行改所有

nonlocal!~nonlocal!运行改函数内哟