Python-回调函数、匿名函数和装饰器

0 前言

>>返回Python系列文章目录<<

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

  1. 首先定义一个装饰器函数decorator,入参是回调函数
  2. decorator函数中定义一个模版函数wrapper,入参为*args, **kwargs,表示接受任意形式的入参
  3. wrapper(*args, **kwargs)函数中调用回调函数func(*args, **kwargs)
  4. 在回调函数上下执行自己要附加的动作,比如计时
  5. 装饰器函数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)

解释一下这个过程,调用一个有装饰器的函数,格式上进行如下转换:

  1. 原格式
@decorator
def func1(a,b):
  # code
  
func1(a,b)
  1. 转换后格式
decorator(func1)(a, b)
  1. decorator(func1)执行结果是return wrapper,所以再次转换为:
wrapper(a, b)
  1. 所以实际上是执行了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

调用一个有参数装饰器的函数,格式上进行如下转换:

  1. 原格式
@decorator(deco_args)
def func1(a,b):
  # code
  
func1(a,b)
  1. 转换后格式
decorator(deco_args)(func1)(a, b)
  1. decorator(deco_args)执行结果是return middle,所以再次转换为:
middle(func1)(a, b)
  1. middle(func1)执行结果是return wrapper,所以再次转换为:
wrapper(a, b)
  1. 最终实际上是执行了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
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值