函数的嵌套与闭包
函数的嵌套是指在函数内再定义一个函数,举个例子:
def outter():
name='jack'#被下层函数使用
age=20#被下层函数使用
address='YN'#被下层函数做返回值
height='178'#没有被下层函数使用
def inner():
new_name=name[0:3]
new_age=age+1
inner_l=locals()
inner_g=globals()
return address,inner_l#address使用outter的
return inner#返回不是值对象,是函数对象
注意到外层函数返回的是内层函数对象,如果我调用:
inner=outter()
结果为:
<function __main__.outter.<locals>.inner()>
可以看出inner是outter的locals()空间下的函数对象,再回顾以前说过,函数名返回的只是函数本身,如果要彻底调用inner,需要写inner(),即outter()();注意一下:outter()只是inner本身
观察内层函数inner,里面有一句:inner_l=locals()
,然后这个inner_l在内层函数结尾时被return了,这说明我们只要调用了inner将可以查看到内层函数的locals()命名空间,所以调用inner()看一下:也就是调用outter()()
('YN',
{'address': 'YN',
'age': 20,
'name': 'jack',
'new_age': 21,
'new_name': 'jac'})
元组里第一个’YN‘应该是address,第二个字典就是inner的locals()空间,发现没有外部函数的height(注意内部函数没有用到height),而其他在外部函数中用到的对象都出现在了inner的空间里,这就是闭包;
可以dir()查看一下内层函数对象包含的对象:
inner=outter()
dir(inner)
发现有一个对象叫inner.__closure__
,我们查看这个对象的内容有:
(<cell at 0x7f271534c828: str object at 0x7f27152d86c0>,
<cell at 0x7f271534ca38: int object at 0xa6a000>,
<cell at 0x7f271534c7f8: str object at 0x7f27152d8688>)
可见,__closure__中保存了inner需要用到的外部变量str:name,int:age,str:address,而height没有用到,所以不在__closure__里面,这就是闭包,闭包指的是:内部函数可以使用外部函数的变量
LEGB访问中的E
现在可以补充E空间了,E就是指闭包对象,只有出现嵌套函数情况才有E空间,访问会从内层函数的locals()开始,到外部函数的locals()即闭包Enclose空间,再到globals(),最后搜索到builtins;
当然,Enclose空间并不完全指外层函数的locals(),准确来说,是外层函数与内层函数相交的那部分对象才是Enclose空间
python装饰器
在前面嵌套函数的基础上,看懂装饰器就会变得很容易,在开始之前,先想想为什么会需要装饰器?
出现它的目的,主要是用于为不同的函数增加相同的功能,减少代码重复,假设有以下函数:
def func1():
print("1")
def func2():
print("2")
def func3():
print("3")
现在要给它们加上同样的功能,我在刚接触python时,会选择无脑地为每个函数重复写上同样的功能:
def func1():
print("1")
print("add func")
def func2():
print("2")
print("add func")
def func3():
print("3")
print("add func")
在发现函数也可以作为参数后,我想到了一个方法,看似简单了一点(注意这不是嵌套函数,因为我没有在函数内定义函数):
def add_func(func):
print("add func")
return func
#调用:参数更换函数名即可,如func1,func2,func3都行
add_func(func1)()
"""
输出
add func
1
"""
但是总感觉不够美观,于是开始使用装饰器,首先装饰器函数的定义需要遵循格式,它是使用嵌套函数定义的:
def outter(func):
print("可以在这里执行增添功能")
def inner():
print("也可以在这里执行增添功能")
#执行被装饰的函数
func()
return None
return inner#外层函数必须返回内层函数对象
#注意装饰器的使用格式
@outter
def myfunc():
print("这里执行被装饰的函数")
#调用:直接写函数名
myfunc()
结果如下:
可以看出,装饰器让函数新增功能的定义和调用更加简洁,在为函数增加功能的同时,不改变函数的调用,使代码易于维护