1. 闭包
在函数内部再定义一个函数,并且内部函数用到了外部函数作用域里的变量(enclosing)
,那么将这个内部函数以及用到的外部函数内的变量一起称为闭包(Closure)
基本格式:
def 外部函数名():
内部函数需要的变量
def 内部函数名()
引用外部的变量
return 内部函数
- 注意:
函数的作用域关系在函数定义阶段就已经固定,与调用位置无关。
无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系。
闭包中不要引用外部函数中任何循环变量或后续会发生变化的变量(延迟计算)
2. 装饰器
2.1 装饰器语法糖
@
符号就是装饰器的语法糖
它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上。和这个函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶上这顶帽子,这顶帽子我们称之为装饰函数
或 装饰器
装饰器的使用方法很固定:
(1) 先定义一个装饰函数(帽子)(也可以用类、偏函数实现)
(2) 再定义你的业务函数、或者类(人)
(3) 最后把这顶帽子带在这个人头上
2.2 装饰器无参数
格式:
def 装饰器外部函数(func): #func 就是被装饰函数
def装饰器内部函数(*args, **kwargs): #装饰器内部函数是一个闭包,它使用了外部函数的变量func装饰函数的功能
装饰器的功能
return func(*args, **kwargs) #执行被装饰函数并返回被装饰函数的返回值
return 返回装饰器内部函数名 #注!返回装饰器内部函数名而不是装饰器内部函数
@装饰器外部函数名 #将被装饰函数传入装饰器,返回装饰器内部函数引用
def 被装饰函数(被装饰函数的参数):
被装饰函数的功能
return 被装饰函数的返回值
接收返回值 = 被装饰函数(被装饰函数的参数) #执行函数,即执行装饰器内部函数,这个闭包使用的func变量指向被装饰函数体
print(接收返回值) #打印一下接收的返回值
2.4 装饰器带参数
根据功能需求,在原来的装饰器外面再加一层函数
格式:
def 需求函数(需求函数参数):
def 装饰器外部函数(func):
def 装饰器内部函数(*args, **kwargs):
装饰器的功能
return func(*args, **kwargs)
return 装饰器内部函数
return 装饰器外部函数
@需求函数('需求函数参数') # 等价于 被装饰函数 = 需求函数('需求函数参数')(被装饰函数) ,即先执行需求函数('需求函数参数'),返回装饰
#(接上一行的解释)器外部函数引用(真正的装饰器),再用装饰器外部函数装饰被装饰函数,返回装饰器内部函数
def 被装饰函数(被装饰函数的参数):
被装饰函数的功能
return 被装饰函数的返回值
接收返回值 = 被装饰函数(被装饰函数的参数) #执行函数,即执行装饰器内部函数,这个闭包使用的func变量指向被装饰函数体
print(接收返回值) #打印一下接收的返回值
2.5 使用@wraps
当打印被装饰函数函数名时,因为被装饰函数是在装饰器内部函数调用所以显示装饰器内部函数名,想打印被装饰函数名时可以使用functools
模块的wraps
装饰器解决这个问题
格式:
from functools import wraps
def 需求函数(需求函数参数):
def 装饰器外部函数(func):
@wraps(func)
def 装饰器内部函数(*args, **kwargs):
装饰器的功能
return func(*args, **kwargs)
return 装饰器内部函数
return 装饰器外部函数
@需求函数('需求函数参数') # 等价于 被装饰函数 = 需求函数('需求函数参数')(被装饰函数) ,即先执行需求函数('需求函数参数'),返回装饰
#(接上一行解释)器外部函数引用(真正的装饰器),再用装饰器外部函数装饰被装饰函数,返回装饰器内部函数
def 被装饰函数(被装饰函数的参数):
被装饰函数的功能
return 被装饰函数的返回值
接收返回值 = 被装饰函数(被装饰函数的参数) #执行函数,即执行装饰器内部函数,这个闭包使用的func变量指向被装饰函数体
print(接收返回值) #打印一下接收的返回值
2.6 多个装饰器装饰同一个函数
格式:
# 装饰器1
def 装饰器外部函数(func): #func 就是被装饰函数
def装饰器内部函数(*args, **kwargs): #装饰器内部函数是一个闭包,它使用了外部函数的变量func装饰函数的功能
装饰器的功能
return func(*args, **kwargs) #执行被装饰函数并返回被装饰函数的返回值
return 返回装饰器内部函数名 #注!返回装饰器内部函数名而不是装饰器内部函数
# 装饰器2
def 装饰器外部函数(func): #func 就是被装饰函数
def装饰器内部函数(*args, **kwargs): #装饰器内部函数是一个闭包,它使用了外部函数的变量func装饰函数的功能
装饰器的功能
return func(*args, **kwargs) #执行被装饰函数并返回被装饰函数的返回值
return 返回装饰器内部函数名 #注!返回装饰器内部函数名而不是装饰器内部函数
@装饰器1的外部函数名 #将被装饰函数传入装饰器,返回装饰器内部函数引用
@装饰器2的外部函数名 #将被装饰函数传入装饰器,返回装饰器内部函数引用
def 被装饰函数(被装饰函数的参数):
被装饰函数的功能
return 被装饰函数的返回值
接收返回值 = 被装饰函数(被装饰函数的参数) #执行函数,即执行装饰器内部函数,这个闭包使用的func变量指向被装饰函数体
print(接收返回值) #打印一下接收的返回值
小结:多个装饰器的顺序,包装时,是从下往上的,也就是被装饰函数头上,哪个离得近就先包装哪个
2.7 基于类实现的装饰器
基于类装饰器的实现,必须实现__call__
和 __init__
两个内置函数。
2.7.1 不带参数的类装饰器
-
__init__
:接收被装饰函数 -
__call__
:实现装饰逻辑
格式:
class 类装饰器函数(object):
def __init__(self, func): #接收被装饰函数
self.func = func
def __call__(self, *args, **kwargs): #实现装饰逻辑
装饰器函数的功能
return self.func(*args, **kwargs)
@类装饰器函数名
def 被装饰函数(被装饰函数的参数):
被装饰函数的功能
被装饰函数(被装饰函数的参数) #执行函数
2.7.2 带参数的类装饰器
带参数和不带参数的类装饰器有很大的不同。
__init__
:不再接收被装饰函数,而是接收传入参数。
__call__
:接收被装饰函数,实现装饰逻辑。
格式:
class 类装饰器(object):
def __init__(self, level='类装饰器函数参数'):
self.level = level
def __call__(self, func): # 接受函数
def 装饰器内部函数(*args, **kwargs):
装饰器的功能
func(*args, **kwargs)
return 装饰器内部函数名 #返回函数
@类装饰器函数名(level='类装饰器函数参数')
def 被装饰函数(被装饰函数的参数):
被装饰函数的功能
被装饰函数(被装饰函数的参数) #执行函数
网友评论