python中闭包与装饰器
前几天学习python装饰器时,看各种例子,上来就是一个嵌套函数,还返回一个函数对象(返回内嵌函数),学得我是一脸懵逼啊,觉得太难懂了。今天又来学习,才发现,原来要理解装饰器,先得了解闭包的知识。没错,就是闭包。
闭包又是个什么东西啊?怎么好像更难懂啊。别着急。。。其实我之前没搞懂的那个嵌套函数就是闭包的写法,下面记录了学习笔记,来一起学习吧。
闭包(closure)
认识闭包
咱们用好理解一点的Python的语言介绍一下,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量
举个栗子:
def func(name):
def inner_func(age):
print 'name:', name, 'age:', age
return inner_func
bb = func('lucy')
bb(26)
# >>> name: lucy age: 26
这里面调用func的时候就产生了一个闭包——inner_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。
闭包的作用
闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数(也即闭包),此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在,这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活。
练习
如下代码,求和,求平均数:
#!/usr/bin/python
# -*-coding:utf-8-*-
def my_sum(*arg):
a = sum(arg)
print a
return a
def my_average(*arg):
b = sum(arg)/len(arg)
print b
return b
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
当调用my_sum时传入的参数有str,调用my_average时传入的参数为空时代码会报错,所有增加代码判断传入的参数
#!/usr/bin/python
# -*-coding:utf-8-*-
def my_sum(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
a = sum(arg)
print a
return a
def my_average(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
b = sum(arg)/len(arg)
print b
return b
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6') # 这2个调用都会报错,所有要加判断,来判断输入的参数
my_average()
那么问题来了,以上2个函数,每个函数中对于传入参数的判断代码是重复的,一旦修改,就需要多个地方修改,工作量就大了,对于代码的维护也不便。那么怎么优化代码了,这时候就可以引入闭包了
#!/usr/bin/python
# -*-coding:utf-8-*-
def my_sum(*arg):
a = sum(arg)
print a
return a
def my_average(*arg):
b = sum(arg)/len(arg)
print b
return b
def dec(func):
def in_dec(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
func(*arg)
return in_dec
my_sum = dec(my_sum)
my_average =dec(my_average)
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6') # 这2个调用都会报错,所有要加判断,来判断输入的参数
my_average()
装饰器(Decorator)
认识装饰器
“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”
其实装饰器就是对闭包的使用
特点:
1、装饰器是用例装饰函数;
2、返回一个函数对象;
3、被标识函数标识符指向:返回的函数对象;
4、语法糖 @
练习
上面闭包的例子,使用装饰器后的代码如下:
#!/usr/bin/python
# -*-coding:utf-8-*-
def dec(func):
def in_dec(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
func(*arg)
return in_dec
@dec
def my_sum(*arg):
a = sum(arg)
print a
return a
@dec
def my_average(*arg):
b = sum(arg)/len(arg)
print b
return b
# my_sum = dec(my_sum)
# my_average =dec(my_average)
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6') # 这2个调用都会报错,所有要加判断,来判断输入的参数
my_average()
实际就是将调用dec函数,使用@dec代替
# my_sum = dec(my_sum)
# my_average =dec(my_average)
调用过程分析:
1、@dec后,my_sum作为一个参数传给dec,dec(my_sum) 返回一个函数对象in_dec
2、返回的函数对象in_dec,由谁来接收呢?my_sum 原来指向的是求和函数,但现在my_sum 作为一个参数传给了dec函数,然后他当作一个enclosing作用域的变量(需要了解函数作用域LEGB),被in_dec函数所使用,so,my_sum 重新赋了值,my_sum = in_dec
3、my_average()实际是调用in_dec(),且装饰器里重新调用了my_average()函数