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

相关内容

热门资讯

Python|位运算|数组|动... 目录 1、只出现一次的数字(位运算,数组) 示例 选项代...
张岱的人物生平 张岱的人物生平张岱(414年-484年),字景山,吴郡吴县(今江苏苏州)人。南朝齐大臣。祖父张敞,东...
西游西后传演员女人物 西游西后传演员女人物西游西后传演员女人物 孙悟空 六小龄童 唐僧 徐少华 ...
名人故事中贾岛作诗内容简介 名人故事中贾岛作诗内容简介有一次,贾岛骑驴闯了官道.他正琢磨着一句诗,名叫《题李凝幽居》全诗如下:闲...
和男朋友一起优秀的文案? 和男朋友一起优秀的文案?1.希望是惟一所有的人都共同享有的好处;一无所有的人,仍拥有希望。2.生活,...
戴玉手镯的好处 戴玉手镯好还是... 戴玉手镯的好处 戴玉手镯好还是碧玺好 女人戴玉?戴玉好还是碧玺好点佩戴手镯,以和田玉手镯为佳!相嫌滑...
依然什么意思? 依然什么意思?依然(汉语词语)依然,汉语词汇。拼音:yī    rán基本解释:副词,指照往常、依旧...
高尔基的散文诗 高尔基的散文诗《海燕》、《大学》、《母亲》、《童年》这些都是比较出名的一些代表作。
心在飞扬作者简介 心在飞扬作者简介心在飞扬作者简介如下。根据相关公开资料查询,心在飞扬是一位优秀的小说作者,他的小说作...
卡什坦卡的故事赏析? 卡什坦卡的故事赏析?讲了一只小狗的故事, 我也是近来才读到这篇小说. 作家对动物的拟人描写真是惟妙...
林绍涛为简艾拿绿豆糕是哪一集 林绍涛为简艾拿绿豆糕是哪一集第三十二集。 贾宽认为是阎帅间接导致刘映霞住了院,第二天上班,他按捺不...
小爱同学是女生吗小安同学什么意... 小爱同学是女生吗小安同学什么意思 小爱同学,小安同学说你是女生。小安是男的。
内分泌失调导致脸上长斑,怎么调... 内分泌失调导致脸上长斑,怎么调理内分泌失调导致脸上长斑,怎么调理先调理内分泌,去看中医吧,另外用好的...
《魔幻仙境》刺客,骑士人物属性... 《魔幻仙境》刺客,骑士人物属性加点魔幻仙境骑士2功1体质
很喜欢她,该怎么办? 很喜欢她,该怎么办?太冷静了!! 太理智了!爱情是需要冲劲的~不要考虑着考虑那~否则缘...
言情小说作家 言情小说作家我比较喜欢匪我思存的,很虐,很悲,还有梅子黄时雨,笙离,叶萱,还有安宁的《温暖的玄》 小...
两个以名人的名字命名的风景名胜... 两个以名人的名字命名的风景名胜?快太白楼,李白。尚志公园,赵尚志。
幼儿教育的代表人物及其著作 幼儿教育的代表人物及其著作卡尔威特的《卡尔威特的教育》,小卡尔威特,他儿子成了天才后写的《小卡尔威特...
海贼王中为什么说路飞打凯多靠霸... 海贼王中为什么说路飞打凯多靠霸气升级?凯多是靠霸气升级吗?因为之前刚到时确实打不过人家因为路飞的实力...
运气不好拜财神有用吗运气不好拜... 运气不好拜财神有用吗运气不好拜财神有没有用1、运气不好拜财神有用。2、拜财神上香前先点蜡烛,照亮人神...