Python-回调函数和装饰器
0 前言
1 回调函数
在任何语言中,模块与模块之间都有上下级关系,比如:
up_file是上层模块,low_file是下层模块
# 上层up_file
from low_file import *
def up_function():
# code
# 下层low_file
def low_function():
# code
在上层模块up_file中使用下层模块low_file的函数low_function()
非常简单,当成本地函数直接调用就行了。但是如果要在下层模块low_file中使用上层模块up_file的函数up_function()
,则需要使用回调:
回调过程:
1、 在下层模块中low_file定义一个函数,入参要求是个函数
# 下层low_file
def low_call(func):
func()
2、 在上层模块中up_file调用这个函数,将自身的函数作为入参放进去,注意不要加括号
# 上层up_file
from A_file import *
def B_callback():
# code
if __name__ == "__main__"
A_call(B_callback)
这个被传入下层的函数B_callback()
就是回调函数
2 匿名函数
有些函数在设计时,需要有一个回调函数作为入参。如果这个回调函数逻辑不复杂,不想完整定义一个函数,可以使用匿名函数
在Python中,不通过def
来声明函数名字,而是通过lambda
关键字来定义的函数称为匿名函数。
2.1 匿名函数一般格式
lambda
函数本质上是一个只有入参和return语句的函数,等效如下:
普通函数:
def func(a, b, c):
return a + b + c
等效lambda
函数表达式为:
lambda args: return
lambda a, b, c: a + b + c
2.2 匿名函数分支格式
lambda
函数可以使用if
判断,表达式为:
lambda args: true_return if condition else false_return
lambda x: x if x % 2 == 0 else x + 1
2.3 匿名函数被调用
在任何需要回调函数作为入参的情况下,都可以直接使用匿名函数
# 使用参数key,根据自定义,按字符串长度来排序:
a = ['dog', 'monkey', 'bird', 'elephant']
b = sorted(a, key=lambda x:len(x))
print(b)
>>>['dog', 'bird', 'monkey', 'elephant']
a = ['dog', 'monkey', 'bird', 'elephant']
b = map(lambda x:x.upper(), a)
print(list(b))
>>>['DOG', 'MONKEY', 'BIRD', 'ELEPHANT']
3 装饰器
3.1 不带参数的装饰器
利用回调函数,Python设计了一种特殊函数,称为装饰器decorator
- 首先定义一个装饰器函数
decorator
,入参是回调函数 - 在
decorator
函数中定义一个模版函数wrapper
,入参为*args, **kwargs
,表示接受任意形式的入参 - 在
wrapper(*args, **kwargs)
函数中调用回调函数func(*args, **kwargs)
- 在回调函数上下执行自己要附加的动作,比如计时
- 装饰器函数
decorator
返回定义的wrapper
函数
import time
def decorator(func):
def wrapper(*args, **kwargs):
startTime = time.time()
ret = func(*args, **kwargs) # 回调函数
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return ret
return wrapper
此装饰器函数可以以@decorator
的形式加在任何函数定义的上一行,被修饰的函数在执行时自动变成装饰器函数的回调函数
@decorator
def func1(a,b):
# code
@decorator
def func2(a,b,c):
# code
if __name__ == '__main__':
ret = func1(a,b)
ret = func2(a,b,c)
解释一下这个过程,调用一个有装饰器的函数,格式上进行如下转换:
- 原格式
@decorator
def func1(a,b):
# code
func1(a,b)
- 转换后格式
decorator(func1)(a, b)
decorator(func1)
执行结果是return wrapper
,所以再次转换为:
wrapper(a, b)
- 所以实际上是执行了
wrapper(a, b)
函数
def wrapper(a, b):
startTime = time.time()
ret = func1(a, b) # 回调函数
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return ret
3.2 带参数的装饰器
带参数的装饰器定义如下,增加一层封装middle
:
def decorator(deco_args):
def middle(func):
def wrapper(*args,**kwargs):
print(deco_args)
ret=func(*args,**kwargs)
return ret
return wrapper
return middle
调用一个有参数装饰器的函数,格式上进行如下转换:
- 原格式
@decorator(deco_args)
def func1(a,b):
# code
func1(a,b)
- 转换后格式
decorator(deco_args)(func1)(a, b)
decorator(deco_args)
执行结果是return middle
,所以再次转换为:
middle(func1)(a, b)
middle(func1)
执行结果是return wrapper
,所以再次转换为:
wrapper(a, b)
- 最终实际上是执行了
wrapper(a, b)
函数,且带入了参数deco_args
def wrapper(a, b):
print(deco_args)
ret = func1(a, b) # 回调函数
return ret
3.3 保留原函数的属性
装饰器运行时有一个问题,在debug时,被装饰的函数func1.__name__消失了,debug信息中只剩下了wrapper
函数。如果想要保留原函数的属性,就可以用到functools.wraps了
functools.wraps
可以将原函数对象的指定属性复制给包装函数对象,默认有 module、name、doc,代码如下:
from functools import wraps
def decorator(deco_args):
def middle(func):
@wraps(func)
def wrapper(*args,**kwargs):
print(deco_args)
ret=func(*args,**kwargs)
return ret
return wrapper
return middle