闭合函数和装饰器
闭包
理解:按我个人的理解闭包就是一个内嵌函数在调用外面一个函数的变量名时被引用,那么这个内嵌函数就被称为闭包函数,简称闭包
闭:代表函数是一个内嵌函数
包:指的是该函数访问了一个来自于外层函数的变量
为函数体传参的方式
方案一:直接使用参数的形式传递
方案二:把函数体想要的参数包给他
闭包的用途
理解:闭包主要运于在你无法动之前已经写的代码下,你想要为原函数传参,就可以使用闭包
def outter(x): # x = 111
def wrapper(): # wrapper = 闭包函数的内存地址
print(x)
return wrapper # 一定不要加括号
# return 闭包函数的内存地址
f1 = outter(111) # f = 闭包函数的内存地址
f2 = outter(222) # f = 闭包函数的内存地址
装饰器
什么是装饰器?
装饰器分为’装饰’与器
装饰:指为一个对象在原本的基础上进行打扮修饰,即在不修改原本代码的情况下为对象添加新功能
器:工具
所以综上所述
装饰器:为对象添加新功能的工具(函数)
为什么要有装饰器
== 软件一旦上线运行后,就应该遵循开发封闭原则:==
== 1.开放指的是对扩展新功能开放==
== 2.封闭指的是对修改源代码封闭==
== 因此我们不能对源代码进行修改,这时候就需要用到装饰器了==
装饰器的实现
装饰器分为:无参装饰器和有参装饰器两种,这两者实现的原理是一样的,都是嵌套函数+闭包+函数对象
的组合使用的产物
无参装饰器
ps:首先我们需要注意的是装饰器的实现你不能改变其本身的调用方式
例如:本来index()是这样调用的,若你使用装饰器为其添加功能后,你依然需要用index()便能调用其原本的功能和新加的功能
接下来我们看一个实现为一个函数添加记录时间功能的例子
import time
#首先我们定义一个原函数
def index():
time.sleep(3)
print("这是原函数")
return 200
#其次我们需要为其添加一个记录其运行时间的功能
#若是常规操作,那我们则需要
a = time.time()
index()
b = time.time()
print(b-a)
#但我们若需要有多个函数需要记录运行时间
#这些代码便成为了重复代码,因此我们需要将其包装
def wrapper(func):
a = time.time()
func()
b = time.time()
print(b-a)
return wrapper
wrapper(index)
#这样子我们也能记录时间,但是我们每次需要记录时间都需要调用的是wrapper()函数,而不是原函数
#我们希望能通过调用原函数的方式使其能实现功能,不然就是改变了他的调用方式
#因为python的万物皆对象的方式我们可以通过令index = wrapper(index)使index同时拥有原功能以及新功能
def wrapper(func):
a = time.time()
func()
b = time.time()
print(b-a)
return wrapper
index = wrapper(index)
#但是通过这个方式我们获得的index每次调用都需要往内部填写一个func而其原来是不需要的
#因此这便等于我们修改了其的调用方式
#因此我们在外面再嵌套一个函数,也就是使用闭包的方式来解决这个问题
def timer(func)
def wrapper()#wrapper装饰,包装
a = time.time()
func()
b = time.time()
print(b-a)
return wrapper#返回一个函数对象
index = timer(index)
index()
当我们传入的参数本身就自带参数的情况下,若是直接运行便会报错
这种时候我们可以使用*args(保存成元组)以及kwrags(保存成字典)的方式保证其不论传入几个参数我们都能使其正常运行 (包括0个)**
import time
def timer(func):
def wrapper(*args,**kwargs):
a = time.time()
func()
b = time.time()
print(b - a)
return wrapper
def index():
time.sleep(3)
print("这是原函数")
return 200
index = timer(index)
index()
python提供了专门的语法来帮助我们替代index = timer(index)的方法,便是再被装饰器的头上使用@+装饰器函数的名称,这样子可以看起来更加简洁(也被称为语法糖)
@timer
def index():
time.sleep(3)
print("原函数")
return 200
有参装饰器
在我们理解了无参装饰器后,对有参装饰器也就不难理解了。
无参装饰器的前提是装饰器内部功能并不需要额外的参数了。
首先:无参装饰器共有两层。
最外面的那层我们需要填写的是函数的名称。
里面那层我们则是需要填写原函数需要的值。因为我们本身不能对原函数进行任何的修改,因此我们不能对着两层进行任何的修改。
其次,而在我们编写程序的时候可能需要我们提供更多的功能,比如认证功能之类。这种时候我们需要获得外界的别的参数,比如用户名与密码,这样子的话只凭装饰器的两层很明显是不够用的。
这种时候我们便需要在外面嵌套第三层的装饰器。这一层是专门用来传递我们最内部函数本身需要的参数的。
import time
def test(username):
def timer(func):
def wrapper(*args,**kwargs):
print(username)
a = time.time()
func()
b = time.time()
print(b - a)
return wrapper
return timer
@test(username='qaq')
def index():
time.sleep(3)
print("原函数")
return 200
index()
最后,我们是否可以对装饰器进行装饰呢,答案是肯定的,如下图所示,我们对test装饰器进行了timmer装饰
@timmer#含义是对下面那个装饰器进行装饰
@test(username='qaq')
def index():
time.sleep(3)
print("原函数")
return 200
index()
装饰器加载的顺序是:自下而上
装饰器功能的实现顺序:自上而下
综上所述,上面的便是无参装饰器以及有参装饰器的所有内容。
接下来是对装饰器内容的扩展
首先我们知道我们是可以在函数内部添加注释的,那么我们有什么方法可以查看这个注释吗
#help方法
def index():
'''这是注释'''
time.sleep(3)
print("原函数")
return 200
print(help(index)) #其实本质是查看他的doc文档
>>>这是注释
但是在被装饰器装饰后,我们其实也可以明白其本质其实是装饰函数的地址被赋予了原函数名
因此若你使用help方法,他查看的也是装饰器的doc文档,看不见原函数内的注释等内容
import time
def test(username):
def timer(func):
def wrapper(*args,**kwargs):
print(username)
a = time.time()
func()
b = time.time()
print(b - a)
return wrapper
return timer
@test(username='qaq')
def index():
'''这是注释'''
time.sleep(3)
print("原函数")
return 200
print(help(index)) #其实本质是查看他的doc文档
>>>qaq
这是首页
3.0008931159973145
Help on NoneType object:
这种时候若我们需要将这装饰函数从本质上包装成原函数,那么我们需要导入
from functools import wraps
#并在我们需要的函数上面添加@wrap(需要包装成的原函数名)
#这样子当别人使用help看见的便是原函数的文档
from functools import wraps
import time
def test(username):
def timer(func):
@wraps(func)
def wrapper(*args,**kwargs):
print(username)
a = time.time()
func()
b = time.time()
print(b - a)
return wrapper
return timer
@test(username='qaq')
def index():
'''这是注释'''
time.sleep(3)
print("原函数")
return 200
print(help(index) #其实本质是查看他的doc文档
>>>这是注释
'''
无参模板
def a(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
print(res)
return res
return wrapper
有参模板
def timmer():
def a(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
print(res)
return res
return wrapper
return a
'''
#将函数放入字典
route_dic = {}
def make_route(name):
def deco(func):
route_dic[name] = func
return deco
@make_route('select') # 1.make_route = make_route(select)--->deco地址(在调用此函数前就运行) = @make_route实际上是@deco 2.func = deco(func)(调用该函数时运行)
def func1():
print('select')
@make_route('insert')
def func2():
print('insert')
@make_route('update')
def func3():
print('update')
@make_route('delete')
def func4():
print('delete')