python 第3章 之三 闭包,模块等

闭包&LEGB法则
所谓闭包,就是将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象
听上去的确有些复杂,还是用一个栗子来帮助理解一下。假设我们在foo.py模块中做了如下定义:
#foo.py
filename = "foo.py"

def call_func(f):
return f() #如前面介绍的,f引用一个函数对象,然后调用它
1
2
3
4
5
在另一个func.py模块中,写下了这样的代码:
#func.py
import foo #导入foo.py

filename = "func.py"
def show_filename():
return "filename: %s" % filename

if name == "main":
print foo.call_func(show_filename) #注意:实际发生调用的位置,是在foo.call_func函数中
1
2
3
4
5
6
7
8
9
当我们用python func.py命令执行func.py时输出结果为:
chiyu@chiyu-PC:~$ python func.py
filename:func.py
1
2
很显然show_filename()函数使用的filename变量的值,是在与它相同环境(func.py模块)中定义的那个。尽管foo.py模块中也定义了同名的filename变量,而且实际调用show_filename的位置也是在foo.py的call_func内部。
而对于嵌套函数,这一机制则会表现的更加明显:闭包将会捕捉内层函数执行所需的整个环境:
#enclosed.py
import foo
def wrapper():
filename = "enclosed.py"
def show_filename():
return "filename: %s" % filename
print foo.call_func(show_filename) #输出:filename: enclosed.py
实际上,每一个函数对象,都有一个指向了该函数定义时所在全局名称空间的globals属性:
#show_filename inside wrapper
#show_filename.globals

{
'builtins': <module 'builtin' (built-in)>, #内建作用域环境
'file': 'enclosed.py',
'wrapper': <function wrapper at 0x7f84768b6578>, #直接外围环境
'package': None,
'name': 'main',
'foo': <module 'foo' from '/home/chiyu/foo.pyc'>, #全局环境
'doc': None

当代码执行到show_filename中的return “filename: %s” % filename语句时,解析器按照下面的顺序查找filename变量:
1.Local - 本地函数(show_filename)内部,通过任何方式赋值的,而且没有被global关键字声明为全局变量的filename变量;
2.Enclosing - 直接外围空间(上层函数wrapper)的本地作用域,查找filename变量(如果有多层嵌套,则由内而外逐层查找,直至最外层的函数);
3.Global - 全局空间(模块enclosed.py),在模块顶层赋值的filename变量;
4.Builtin - 内置模块(builtin)中预定义的变量名中查找filename变量;
在任何一层先找到了符合要求的filename变量,则不再向更外层查找。如果直到Builtin层仍然没有找到符合要求的变量,则抛出NameError异常。这就是变量名解析的:LEGB法则。
总结:
1.闭包最重要的使用价值在于:封存函数执行的上下文环境;
2.闭包在其捕捉的执行环境(def语句块所在上下文)中,也遵循LEGB规则逐层查找,直至找到符合要求的变量,或者抛出异常。

猜你喜欢

转载自blog.51cto.com/huangsheng2/2114597