挂钩函数——让接口接收函数而不是类的实例
创始人
2024-02-14 15:10:06
0

挂钩函数——让接口接收函数而不是类的实例

1.概述

标题中的接口名称是函数的统称,指的是函数。

在开发业务中如果遇到流程类场景,通过会有多个分支流程。一般我们都是为每个分支写一个流程代码,实现功能。但是这样的写法在每个分支中会存在一些重复代码,不利于维护。
上面这类的场景我们使用挂钩函数来开发是一个不错的选择,代码结构简单,支持左开右闭原则,易扩展和维护。

钩子函数和装饰器使用场景区别

装饰器是一个面向切面编程的方式,通常加载目标函数上,当运行目标函数前每次都会自动调用装饰器函数。特别适合为目标函数执行前后增加额外辅助功能。
装饰器特点:

  • 每次都运行
  • 为目标函数提供额外功能,例如参数校验,打印日志等。

钩子函数是作为参数传入目标函数,当运行目标函数时只有符合预期条件时才会调用钩子函数,通过钩子函数改变目标函数执行逻辑,输出结果。
钩子函数特点:

  • 只有符合预期条件才会执行钩子函数
  • 钩子函数作为参数传入目标函数
  • 钩子函数运行结果作为目标函数的参数直接参与到目标函数运行逻辑中,用来改变目标函数运行逻辑。

2.如何使用挂钩函数

下面通过一些使用示例先带大家体验下使用挂钩函数开发的好处,然后在下面会介绍如何在在开发中创建符合业务的挂钩函数,将它的便利带到业务中。

2.1.挂钩函数基本使用

python有许多内置的函数,都支持挂钩函数,允许我们传入一个函数来定制它的行为。作为参数的函数称为挂钩函数。
大家对挂钩函数的叫法也不同,比如回调函数,钩子函数,其实本质上指的都是同一个实质。

下面用python内置的defaultdict函数来介绍下挂钩函数使用例子。

这个示例是通过挂钩为defaultdict函数定制不同的行为,默认情况下defaultdict函数遇到不存在的key会返回一个默认值,我们为默认值提供一个挂钩函数。
defaultdict函数挂上挂钩函数后,遇到不存在的键就输出log_missing字符串。

from collections import defaultdict
# 定义挂钩函数
def log_missing():print('log_missing')return 0current = {'green':12, 'blue':13}
increments = [('red', 1),('blue', 2),('orange', 3),]# defaultdict方法第一个参数是key不存在时返回一个默认值,第二个参数是向字典添加数据
result = defaultdict(log_missing, current)
print(f'Before: {dict(result)}')
# 遍历列表,获取元组数据 key=red, amount=1
for key, amount in increments:# 当访问字典中key不存在时候调用调用log_missing函数返回结果,log_missing就是一个钩子函数。result[key] += amount
print(f'after:{dict(result)}')

运行上面的代码,在访问result字典key时red和orange两个键不存在,执行了挂钩函数,输出了两次log_missing字符串

Before: {'green': 12, 'blue': 13}
log_missing
log_missing
after:{'green': 12, 'blue': 15, 'red': 1, 'orange': 3}

2.2.有状态的挂钩函数

上面的挂钩函数是一个简单没有状态的函数,如果下面需求希望统计defaultdict函数一共遇到了多少次键缺失的情况,实现这个功能,可以采用闭包来实现有状态的挂钩函数。

def increment_with_report(current, increments):added_count = 0# 闭包处理键不存在的逻辑def missing():nonlocal  added_countadded_count += 1return 0# 创建字典,第一个参数调用闭包处理键缺失逻辑,第二个参数向字典添加数据result = defaultdict(missing, current)for key, amount in increments:# 访问字典中的key,如果不存在就调用闭包函数result[key] += amountreturn result, added_countresult, count = increment_with_report(current, increments)
print(f'result:{dict(result)}, count:{count}')

运行上面的代码,统计出了key缺失的次数。

result:{'green': 12, 'blue': 15, 'red': 1, 'orange': 3}, count:2

上面使用闭包实现了有状态的挂钩函数,increment_with_report函数由于嵌套了闭包代码也复杂了,同时不易扩展。
下面使用类来设计有状态挂钩函数

2.3.像调用普通函数一样调用类对象

把有状态的闭包所具备的行为,改用辅助类来实现,要比前面increment_with_report函数更加清晰。
在类中使用了call魔法方法,可使这个类的对象能够像函数那样调用(defaultdict(counter, current)),即便初次看到这段代码也能明白这个类的主要目标。因为你应该会注意到这比较明显的call方法,暗示这个类可以项有状态闭包那样使用。

当我们使用类创建挂钩函数时,通过call函数实现了让接口(defaultdict函数)接收函数而不是类的实例。

# 通过类构建有状态的挂钩函数
class BetterCountMissing:def __init__(self):self.added = 0# 挂钩函数核心代码def __call__(self):self.added +=1return 0counter = BetterCountMissing()# call方法使得类的对象能够像函数那样调用,直接将对象传入,他会自动执行call方法内容。
result = defaultdict(counter, current)
for key, amount in increments:result[key] += amountprint(f'result:{dict(result)}, count:{count}')

3.为自己的业务应用挂钩函数

前面通过几个小示例展示了如何使用钩子函数,并且体会到了通过钩子函数改变了defaultdict的行为,当defaultdict需要不同的行为时,我们只需要为他传入对应的挂钩函数即可,不需要修改业务代码。实现了左开右闭原则,同时代码维护变得简单了许多。

当我们在开发过程中如果遇到分支流程时就可以考虑使用钩挂函数解决,下面通过一个示例介绍如何将挂钩设计思想带入业务。

下面创建一个getOddNumber函数,它的作用和python内置的defaultdict函数角色是一样的接收一个挂钩参数。为该函数传入不同的挂钩函数实现定制它的行为。

# 回调函数1
# 生成一个2k形式的偶数
def double(x):return x * 2# 回调函数2
# 生成一个4k形式的偶数
def quadruple(x):return x * 4# 目标函数:相当于defaultdict函数角色
# 接受一个挂钩函数作为参数
# 返回一个奇数
def getOddNumber(k, getEvenNumber):return 1 + getEvenNumber(k)k = 1
# 当需要生成一个2k+1形式的奇数时
i = getOddNumber(k, double)
print(f'2k+1形式的奇数:{i}')
# 当需要一个4k+1形式的奇数时
i = getOddNumber(k, quadruple)
print(f'4k+1形式的奇数:{i}')
# 当需要一个8k+1形式的奇数时
i = getOddNumber(k, lambda x: x * 8)
print(f'匿名钩子函数扩展功能:{i}')

运行上面的代码

2k+1形式的奇数:3
4k+1形式的奇数:5
匿名钩子函数扩展功能:9

相关内容

热门资讯

评书童林传250回的和300回... 评书童林传250回的和300回的有什么不一样?内容上是完全一样的,只是在剪辑时语速有所不同。 ...
为啥 有声小说凡人修仙传 和 ... 为啥 有声小说凡人修仙传 和 修真世界 很久都不更新了呢? 播音大灰狼去干吗了?凡人修仙传 和 修真...
月宫的主人不是嫦娥,你知道《封... 月宫的主人不是嫦娥,你知道《封神榜》中姜子牙封了谁吗?大家口中所说的嫦娥在历史上,其实并不是一个人,...
华软科技涨1.15%,成交额6... 7月1日,华软科技盘中上涨1.15%,截至09:57,报6.17元/股,成交6068.05万元,换手...
时间久了是不是真的会忘记? 时间久了是不是真的会忘记?如果那时是真的伤到了心,是不会忘记的,只会淡化,再久的时间想起来还是会有一...
菲沃泰涨1.21%,成交额66... 7月1日,菲沃泰盘中上涨1.21%,截至09:58,报16.76元/股,成交662.09万元,换手率...
党员总数超28.77万名 基层... 来源:本站原创  安庆市委组织部最新党内统计数据显示,截至2024年底,安庆市共有中国共产党党员28...
三棵树成立新材料科技子公司 转自:证券时报人民财讯7月1日电,企查查APP显示,近日,三棵树(海南)新材料科技有限公司成立,注册...
正海生物涨1.07%,成交额9... 7月1日,正海生物盘中上涨1.07%,截至09:43,报20.72元/股,成交936.02万元,换手...
奥联电子跌1.01%,成交额1... 7月1日,奥联电子(维权)盘中下跌1.01%,截至09:44,报15.69元/股,成交1018.39...