闭包和装饰器的理解
一、闭包
先说一下在python中闭包的概念:在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。
举一个很简单的例子:
def outer(a, b):
def inner(c):
return a*b/c
return inner
o1 = outer(3,4)
o2 = outer(5,6)
print(o1(2)) # 6.0
print(outer(3,4)(2)) # 6.0
print(o2(3)) # 10.0
print(outer(5,6)(3)) # 10.0
上述例子中,函数inner与变量a,b构成闭包。在创建闭包的时候,我们通过outer的参数a,b说明了这两个变量的取值,根据传递参数的不同,就可以获得不同表现函数。所以闭包也具有提高代码可复用性的作用。
注意:由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存。
二、装饰器
装饰器在python中是一个非常重要的功能,简单来说python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
先看一段代码:
def set_func(func):
print("call_func外部")
def call_func(*args, **kwargs):
print("call_func执行了")
return func(*args, **kwargs) # 拆包
return call_func
def test(*args, **kwargs):
print("test函数执行了----->%d" % args)
return "test返回值"
test = set_func(test)
t = test(10)
print(t)
输出结果:
call_func外部
call_func执行了
test函数执行了----->10
test返回值
为什么会是这样呢?主要原因是执行了这条语句:test = set_func(test)
,我的理解如下:
使用装饰器后的代码:
def set_func(func):
print("call_func外部")
def call_func(*args, **kwargs):
print("call_func执行了")
return func(*args, **kwargs) # 拆包
return call_func
@set_func
def test(value, *args, **kwargs):
print("test函数执行了----->%d" % value)
return "test返回值"
t = test(10)
print(t)
输出结果:
call_func外部
call_func执行了
test函数执行了----->10
test返回值
与上一段代码执行结果相同,改动只是去掉了原来的test = set_func(test)
,而在一个test函数前加上@set_func,这便是装饰器。其主要作用就是你想在原来程序实现的功能上再加一些功能,而且不必在你原来写好的代码中进行修改,只需加上装饰器即可,这便是使用装饰器的好处。
再看一个多个装饰器装饰一个函数的例子:
def set_func1(func):
print("***装饰器1开始装饰***")
def call_func(*args, **kwargs):
print("***装饰器1的功能***")
return func(*args, **kwargs)
return call_func
def set_func2(func):
print("***装饰器2开始装饰***")
def call_func(*args, **kwargs):
print("***装饰器2的功能***")
return func(*args, **kwargs)
return call_func
@set_func1
@set_func2
def test(*args, **kwargs):
print("test1运行.........")
return "test的返回值"
t = test()
print(t)
输出结果:
***装饰器2开始装饰***
***装饰器1开始装饰***
***装饰器1的功能***
***装饰器2的功能***
test1运行.........
test的返回值
所以在一个函数被多个装饰器装饰时(自上而下看装饰器):
- 装饰器装饰的顺序:最下----------->最上
- 装饰器功能执行的顺序:最上---------->最下
总结:
装饰器本质上是一个 Python 函数,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能。装饰器的返回值也是一个函数的对象,它经常用于有切面需求的场景。 比如:插入日志、性能测试、事务处理、缓存、权限的校验等场景 有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。