挂钩函数——让接口接收函数而不是类的实例
创始人
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

相关内容

热门资讯

世事繁华不及你惊鸿一瞥什么意思... 世事繁华不及你惊鸿一瞥什么意思?理解这句话首先要明白惊鸿一瞥的意思。那惊鸿一瞥什么意思呢——只是匆匆...
朋友梦见我在火里让她救我是什么... 朋友梦见我在火里让她救我是什么意思?如果你们是单纯的朋友可能说明友谊很好吧,如果不是单纯的,那可能是...
猜谜啦!猜谜啦!很多谜语哦! 猜谜啦!猜谜啦!很多谜语哦!1,开方2,3.4,5,圆周率6。王,8,丕9,10,丰11,晶12,偶...
女孩子说感觉有点怪 女孩子说感觉有点怪这对她来说比较突然,而且也许她现在自己也不能分辨出她对你的感觉到底是喜欢还是好朋友...
南京银行股份有限公司关于实施“... 证券简称: 南京银行 证券代码: 601009 编号: 2025-052优先股简称:南银优1 优先股...
科技赋能“气象+”,一场助企富... (来源:工人日报) “8月头季稻卖一块八毛,10月份再收一季,两季加起来一亩地收上千元!”四川...
取一杯天上水是指月光吗 取一杯天上水是指月光吗不是。是指下雨的时候接一杯天上掉下来的雨水,最纯洁的水最有灵性的水。这杯水在晚...
为什么越来越觉得赚钱比男朋友重... 为什么越来越觉得赚钱比男朋友重要?我也这么认为,因为男朋友不够好。还没遇到真心待自己的人或者,是因为...
腐团儿现实中有男朋友吗? 腐团儿现实中有男朋友吗?腐团儿现实中有男朋友吗?这个谁知道呢一或者暗中有也不一定这种明星私事我也不是...
元宵与汤圆有啥区别 元宵与汤圆有啥区别元宵和汤圆的区别:1.馅料选择不同,汤圆馅偏软,咸甜荤素选择众多,元宵馅偏硬,以甜...