Python作用域总结

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 = 1
b = 2

def 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 defined

print(a)        #打印 1  直接在 Global 中找

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

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

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

a = 1
def 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 inner

inner = 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 inner

inner = wrapper()
print(inner())
print(inner())

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


其他问题:

a = 1

def 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 命名空间的内容。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值