Python作用域总结

来源:互联网 发布:自动对比度算法 编辑:程序博客网 时间:2024/06/04 19:56

Python特有的语法规则和诸如Java等其他静态类型语言有很大不同,而作用域规则则是其一,初识Python会遇到一些问题,现在对Python作用域相关的知识做个总结。

Python作用域规则一句话概括就是 LEGB 规则; L 代表 Local、E 代表 enclosing、G 代表 Global、B 代表 Builtin

作用域由 Python 的代码文本决定,一个模块定义了一个 Global 作用域、一个函数定义一个 Local 作用域; 作用域与一个名字空间一一对应,名字空间是一个mapping对象,它存储了当前作用域中的变量名字以及名字所绑定的对象。不同作用域中的变量名是不相关的。大概关系如下图:
这里写图片描述

假设一个模块中定义一个函数,在函数外面定义了变量a,b; 在函数中定义变量a,c, 模块本身也有Local命名空间,只是和 Global 是同一个命名空间。

除了 Global 和 Local 作用域,还有一个最顶层的作用域 – Builtin 作用域。 Builtin 作用域对应于 Builtin 命名空间,里面包含了内置函数和其他一些内置的东西。既然是系统内置的模块,地位自然非同一般,可以认为在任何模块作用域外面都有一层 Builtin 作用域。

当引用一个变量时,会首先在当前 Local 作用域的命名空间中查找,没找到就去 Global 命名空间查找,再没找到就会去 Builtin 命名空间查找,再没找到就会抛出异常。

a = 1b = 2def func():    a = 3    c = 4    print(a)    #打印3  Local 中找到,直接打印    print(b)    #打印2  Local 中不存在,去 Global 找    print(abs(-5))    #打印5 abs先去 Local 再去 Global找,都没找到,去 Builtin 中找    #print(haha)       #报错,name 'haha' is not definedprint(a)        #打印 1  直接在 Global 中找

在 func 中通过 b = 10 这样的语句是不会修改 Global 中的 b 的,而是会在 Local 命名空间中添加一个名字 ‘b’ 并绑定值为 10
这里写图片描述

要想修改 Global 中的 b, 需要这样
这里写图片描述

前面介绍了 L G B 所代表的作用域以及它们之间的关系,还有查找一个变量的顺序 L -> G -> B,下面介绍比较特殊的 E。
当在函数内部再定义一个函数时,就形成了函数嵌套,形成了闭包,而内层函数的直接外部作用域是外层函数,这块作用域就是 enclosing, 例:

a = 1def wrapper():    a = 2    def inner():        print(a)    inner()wrapper() #执行这句后,会打印2

这里内层函数会先找最内层作用域,即 inner 的 Local 命名空间,没有找到则会到 wapper 的 Local 命名空间找,找到后打印,找不到时才会再往 Global 中去找。 这里在 inner 中直接去修改 wrapper 中的变量还是不起作用的,只会在inner 的 Local 命名空间中添加名字和值,要想修改 enclosing 作用域中的值,
需要这样:
在 Python2 中:

def wrapper():    a = 1    count = [a]    def inner():        count[0] += 1        print 'a:', a        return count[0]    return innerinner = wrapper()print inner()print inner()#打印# a: 1# 2# a: 1# 3

这里是借用一个列表间接修改,实际 a 的值并没有变。

Python3中,提供了一个 nonlocal 关键字,用于直接修改闭包变量

def wrapper():    a = 1    def inner():        nonlocal a        a += 1        return a    return innerinner = wrapper()print(inner())print(inner())

至此所有的作用域介绍完毕,总的来说当寻找某个变量时从最内层开始找,按照
L -> E -> G -> B 的顺序逐层查找。


其他问题:

a = 1def func():    print(a)    a = 1    print(a)func()

执行上面的代码会报错 UnboundLocalError: local variable ‘a’ referenced before assignment,这是因为在执行第一句 print 时直接就去 Local 中去找了,并且还找到了,但是不幸的是,当前 Local 中的 a 虽然找到了,但是还不可用,赋值语句在下面,Python 是在编译完成后,未执行程序时,就已经知道了 Local 作用域中藏着一个 a,这体现了 Python 作用域的静态性。

在作用域的问题上,只要记住以文本定义为准,而不看在哪里调用的,可以理解为在 python 解释器执行代码时,定义一个函数时,就已经将 Global 和 Local 命名空间绑定到函数对象上了,这个 Global 和 Local 就是函数执行时的环境。

另外内置函数 globals() 和 locals() 分别返回了当前 Global 命名空间和 Local 命名空间的内容。

原创粉丝点击