闭包函数与装饰器的应用
1 > 闭包函数
1.1 > 闭包函数的特征
闭包函数两大特征。第一个就是它是定义一个函数内部的函数,第二个就是内部的函数它会使用外层函数名称空间的名字。其格式如下:
def outer() # 1.创建函数
x = 99 # 3.创建变量x 在outer的局部名称空间当中
def inner(): # 4.创建函数 inner()
print('from outer >>>> inner', x)
return inner # 5.返回 inner 函数名指向的是函数当中的函数体代码块
res = outer() # 2.调用outer()函数将返回值赋值给res
res() # 6.此时res 变成了函数名inner 调用函数inner
# 打印 from outer >>>> inner 99
1.2 > 闭包函数的实际应用
闭包函数是给函数体传参的另一种方式,传参的方式一共有两种方式。其格式如下。
1.2.1 > 函数体传参的方式1:形参
def inde(username):
print(username)
# 函数体代码需要什么就可以在形参中写什么
index('jason')
1.2.2 > 函数体传参的方式2:闭包
def outer(): # 1.创建外层函数outer()
username = 'jason' # 3.定义变量uersname 赋值为'jason'
def index(): # 4.创建内部函数index()
print(username) # 7.打印 ‘jason’
return index # 5.将函数名返回
res = outer() # 2.调用外层函数outer(),将返回值赋值给res
res() # 6.此时res 变成了函数名index 调用函数index
代码中最后一步打印username,在index的局部空间名称当中没有找到就回去外层的outer()函数的局部名称空间寻找username,所有最后会打印outer()函数内的局部名称空间中的username。右或者你可以直接将username当作是形参赋值给内层的index()函数,例如:
def outer(username):
def index():
print(username)
return index
res = outer('bob')
res()
res1 = outer('kevin')
# 此时会打印‘bob’和'kevin'
2 > 装饰器
2.1 > 装饰器简介
装饰器的本质其实就是,在不改变被装饰对象原有的‘调用方式’和‘内部代码’的情况下给被装饰对象添加新的功能。并且,装饰器的原则就是,对外扩展开放,对修改封闭。例如我们对下面的一段代码添加装饰器:
import time
def index():
time.sleep(3) # 让代码停顿3秒后执行
print('from index')
start_time = time.time() # 函数执行之前获取一个时间戳
index()
end_time = time.time() # 函数执行之后获取一个时间戳
print(end_time - start_time) # 两个时间戳的差值就是函数的执行时间
上面的一段代码适用于计算代码的执行时间进行计算的一种方式,但是这个只可以对一个函数进行操作,那我们想使用这一段代码重复利用该如何操作呢? 代码如下:
import time # 1.定义一个时间
def index(): # 2. 定义一个函数 index()
time.sleep(3) # 8.让代码停顿3秒后执行
print('from index') # 9.打印 from index
def get_time(func): # 3. 定义一个函数 get_time()
start_time = time.time() # 6.函数执行之前获取一个时间戳
func() # 7.调用函数index()
end_time = time.time() # 10.函数执行之后获取一个时间戳
print(end_time - start_time) # 11.打印 两个时间戳的差值就是函数的执行时间
def home(): # 4. 定义一个函数 home()
time.sleep(3)
print('from home')
get_time(index) # 5.调用get_time函数将index函数名当实参传入
get_time(home) # 12.调用get_time函数将home函数名当实参传入,继续上一个操作
# 打印
# from index
# 3.005337953567505
# from home
# 3.0100765228271484
这种方式就达到了装饰器的一个功能,就是不改变内部代码,但是调用方式还是改变了,所有我们将这个进一步优化,代码如下。
import time
def index():
time.sleep(1)
print('from index')
def home():
time.sleep(3)
print('from home')
print(home)
def outer(func): # 真正的index被outer局部名称空间存储了
def get_time():
start_time = time.time() # 函数执行之前获取一个时间戳
func() # 调用了真正的index函数
end_time = time.time() # 函数执行之后获取一个时间戳
print(end_time - start_time) # 两个时间戳的差值就是函数的执行时间
return get_time
# 上面的步骤跟上面的方式类似 就是下面用来一招狸猫换太子的方法
index = outer(index) # 定义一个也叫index的变量接收 outer()函数的返回值
index() # index() 就会直接调用
home = outer(home) # 定义一个也叫home 的变量接收 outer()函数的返回值
home() # home() 就会直接调用
# 打印
# from index
# 1.0101003646850586
# from home
# 3.012511968612671
上面的操作用了一种狸猫换太子的方式将调用方式强行转换成它的原本的调用方式,完成了装饰器的特点。创建了一个简化版的装饰器。
2.2 > 进阶版装饰器
为了使用装饰器的时候,传入的位置参数或者是关键字参数传值时。就需要使用 *args和**kargs来接收这些参数,防止调用时无参或者有参时报错。于是我们就对简易版装饰器又进行了以下优化。这个进阶版装饰器就解决的就是参数的问题。
def outer(func_name):
def get_time(*args, **kwargs):
start_time = time.time()
func_name(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
return get_time
2.3 > 完整版装饰器
进阶版的装饰器解决了参数的传入问题,那么当我们遇到返回值出现问题时怎么办呢,我们就可以使用我们这个完整版的装饰器。
def outer(func_name):
def get_time(*args, **kwargs):
start_time = time.time()
res = func_name(*args, **kwargs) # 执行真正的index函数
end_time = time.time()
print(end_time - start_time)
# return '不要急躁' # 如何在此处返回真正index函数的返回值
return res
return get_time
2.4 > 装饰器模板
根据上面的几种版本的装饰器我们可以写出一套模板,如下:
def outer(func_name):
def inner(*args, **kwargs):
print('执行被装饰函数之前 可以做的额外操作')
res = func_name(*args, **kwargs)
print('执行被装饰函数之后 可以做的额外的操作')
return res
return inner
3 > 装饰器语语法糖
这里的语法糖的作用就是仅仅是为了美化代码块,使代码更加的简洁和好看 !!!例如:
def outer(func_name):
def inner(*args, **kwargs):
print('执行函数之前的操作')
res = func_name(*args, **kwargs)
# 额外操作
return res
return inner
@outer # 等价于 index = outer(index)
def index(*args, **kwargs):
print('from index')
# index = outer(index) # 总感觉这一行代码有点low!!!
@outer # 等价于 home = outer(home)
def home(*args,**kwargs):
print('from home')
print(index)
print(home)
法语糖我们在使用时需要注意两点。第一就是,在使用时最好紧跟着被装饰的对象上方,第二个就是,语法糖它会自动将下面紧挨着的函数名传给@后方的函数进行调用。
4 > 装饰器修复技术
上面说过我们在使用装饰器的时候是用了一招狸猫换太子的招数,将变量名改成与函数名同名。但是其内部真实的情况却不是,是一个新的内存空间,那么我们想将这个狸猫换成真正的太子怎么办呢,就要用到我们的修复技术了。
就是在装饰器内部加上以个固定搭配用于修复,代码如下:
from functools import wraps
def outer(func_name):
@wraps(func_name)
def inner(*args, **kwargs):
print('执行被装饰对象之前可以做的操作')
res = func_name(*args, **kwargs)
return res
return inner
@outer
def index():
print('from index')
@outer
def home():
'''这是home函数的注释'''
print('from home')