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

相关内容

热门资讯

淮南市田家庵区发展和改革委员会... 转自:安徽纪检监察据安徽纪检监察消息,淮南市田家庵区发展和改革委员会主任李然涉嫌严重违纪违法,目前正...
代工期间抽检未通过 娃哈哈称已... 转自:消费日报官方微博 【代工期间抽检未通过 #娃哈哈称...
东来技术涨2.27%,成交额2... 5月15日,东来技术盘中上涨2.27%,截至14:19,报22.95元/股,成交2762.53万元,...
柳覃高速关键性工程加禄隧道顺利... 5月15日,由中铁二十局集团承建的广西柳州至覃塘高速公路全线最长隧道——加禄隧道顺利贯通,为后续线路...
汉商集团涨2.10%,成交额1... 5月15日,汉商集团盘中上涨2.10%,截至14:18,报8.77元/股,成交1.27亿元,换手率5...
多个区域手机自动接入信号! 北... 近日,北京不少市民发现手机右上角出现“5G-A”标识,伴随而来的是显著提升的网络速度,尤其是在一些人...
文明河北丨曲周公益红娘志愿服务... 转自:河北新闻网河北日报讯(记者赵泽众)近日,曲周县新成立了353支新时代公益红娘志愿服务队,实现县...
衢州发展跌2.25%,成交额1... 5月15日,衢州发展盘中下跌2.25%,截至14:03,报2.61元/股,成交1.32亿元,换手率0...
甘肃陇南:“流动蓝”翻越千重岭... 中新网兰州5月15日电 (高展 邓鹏银)陇南,地处甘肃东南部,秦巴山区、青藏高原与黄土高原在此交汇,...
张江高科跌2.02%,成交额3... 5月15日,张江高科盘中下跌2.02%,截至14:05,报25.21元/股,成交3.18亿元,换手率...
翻山成功,全新突破!郑钦文生涯... 中新网北京5月15日电(记者 刘星晨)北京时间15日,WTA1000罗马站女单1/4决赛中,8号种子...
抓住黄金4分钟,小伙的急救有多... 来源:央视新闻微信公众号 日前,山东小伙姜昭鹏为救突发心梗休克的同学,导致职教高考语文缺考,引发网友...
从“量身定制”到“拎包入住” ... 转自:人民论坛最近,山西熙峰电气有限公司新员工桑彦敏拖着行李箱,走进襄垣经开区未来科技产业园13号职...
信用减值超80亿元,哈尔滨银行... 华夏时报(www.chinatimes.net.cn)记者 卢梦雪 见习记者 张萌 北京报道作为东北...
红色“浪潮”激荡滨江 舞台剧《浪潮》剧照 在左联成立95周年之际,聚焦革命烈士与文学志士的舞台剧...
落实发展,宝山村要培养更多“李... 转自:推广为加快中国爽村建设,落实“安逸四川、爽在宝山”系列工作安排,2025年5月14日著名管理学...
挑大梁·市县展担当 | 沛县县... 编者按深入学习贯彻习近平总书记参加江苏代表团审议时的重要讲话精神,是当前和今后一个时期江苏全省上下的...
观?点|在江苏 打开博物馆的N... 5月18日是第49个“国际博物馆日”,今年的主题是“快速变化社会中的博物馆未来”。观众在苏州湾博物馆...
中铁国资武汉铁路桥梁职业学院开... 原标题:中铁国资武汉铁路桥梁职业学院开展2025年职教活动周纪实中国铁路工程集团有限公司(简称“中国...
晶合集成跌2.00%,成交额9... 5月15日,晶合集成盘中下跌2.00%,截至13:44,报20.55元/股,成交9862.64万元,...