Python里的自定义回调函数
最编程
2024-02-11 22:38:04
...
目录
-
python hook 机制
-
一、 概念
- 1、 hook概述
- 2、 hook
- 二、 示例
-
一、 概念
python hook 机制
一、 概念
1、 hook概述
hook,又称钩子,在C/C++中一般叫做回调函数。钩子是从功能角度描述这种编程模式,回调则是从函数调用时间角度描述的。通常理解的hook是在一个已有的方法上加入一些钩子,使得在该方法执行前或执行后另在做一些额外的处理。如我们熟知的windows系统消息响应事件,鼠标点击对程序产生的影响是有程序自己决定的,但是程序的执行是受制于框架(windows系统),框架提供了一些通用的流程执行,但是往往框架或流程在设计时无法完全预料到以后的使用会有什么新需求,或者有些行为只有在运行时才能确定的。这就产生了回调的需求,即用户提供需求,框架负责执行,流程先于具体需求,当触发或者满足某种条件时,执行Hook函数。hook函数的数据也是由用户自己提供的,框架只负责流程执行,这样框架的通用性就能大大提高。
2、 hook
钩子函数:就是把我们自己实现的hook函数在某一时刻挂接到目标挂载点上。
- hook函数:就是我们自己实现的函数,函数类型与挂载点匹配(返回值,参数列表)
- 挂接:也就是hook或者叫注册(register),使得hook函数对目标可用
- 目标挂载点:也就是挂我们hook函数的地方
二、 示例
import time
class LazyPerson(object):
def __init__(self, name):
self.name = name
self.watch_tv_func = None # 目标挂载点
self.have_dinner_func = None # 目标挂载点
def get_up(self):
"""起床"""
print("%s get up at:%s" % (self.name, time.time()))
def go_to_sleep(self):
"""睡觉"""
print("%s go to sleep at:%s" % (self.name, time.time()))
def register_tv_hook(self, watch_tv_func): # 挂接
"""注册看电视的钩子函数"""
self.watch_tv_func = watch_tv_func
def register_dinner_hook(self, have_dinner_func):
"""注册吃晚餐的钩子函数"""
self.have_dinner_func = have_dinner_func
def enjoy_a_lazy_day(self):
"""一天的生活"""
self.get_up()
time.sleep(2)
# 如果注册了钩子函数
if self.watch_tv_func is not None:
self.watch_tv_func(self.name)
else: # 没有注册钩子函数
print("no tv to watch")
time.sleep(2)
# have dinner --> check the have_dinner_func(hooked or unhooked) --> hooked
if self.have_dinner_func is not None:
self.have_dinner_func(self.name)
else: # unhooked
print("nothing to eat at dinner")
time.sleep(2)
self.go_to_sleep()
def watch_daydayup(name): # hook函数
print("%s : The program ---day day up--- is funny!!!" % name)
def watch_happyfamily(name):
print("%s : The program ---happy family--- is boring!!!" % name)
def eat_meat(name):
print("%s : The meat is nice!!!" % name)
def eat_hamburger(name):
print("%s : The hamburger is not so bad!!!" % name)
def test():
lazy_tom = LazyPerson("Tom")
lazy_jerry = LazyPerson("Jerry")
# register hook
lazy_tom.register_tv_hook(watch_daydayup)
lazy_tom.register_dinner_hook(eat_meat)
lazy_jerry.register_tv_hook(watch_happyfamily)
lazy_jerry.register_dinner_hook(eat_hamburger)
# enjoy a day
lazy_tom.enjoy_a_lazy_day()
lazy_jerry.enjoy_a_lazy_day()
test()
上一篇: Hook功能与装饰器简介
下一篇: 回调函数和钩子函数
推荐阅读
-
Python 中回调的含义解释
-
简要介绍 select、iocp、epoll、kqueue 和各种 I/O 复用机制--轮询。这是因为它们被回调所取代。想想看,当有很多套接字时,每次选择都必须通过遍历 FD_SETSIZE 套接字来调度,无论哪个套接字处于活动状态,都要遍历一次。这将浪费大量的 CPU 时间。如果能为套接字注册一个回调函数,在套接字处于活动状态时自动执行此操作,就能避免轮询,而这正是 epoll 和 kqueue 所要做的。 Windows 还是 *nix(IOCP 或 kqueue/epoll)?
-
python 使用多处理模块来实现对回调函数的异步调用。
-
XGB-25:回调函数 - 定义您自己的回调函数
-
php 请求接口和在 php 中转换 json 和数组、函数、匿名函数、匿名函数和引用变量的闭包、更改变量值、匿名函数的回调、 - php 学习路径 - php中文网博客
-
回调函数的 C 语言实现 - 二、回调函数的情况
-
js 中的匿名函数和回调函数
-
如何用 capl 语言在 python 中实现回调函数(推荐)
-
透彻理解 Python 中的回调函数 (callback)
-
@Validated和@Valid区别-1.分组 @Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。没有添加分组属性时,默认验证没有分组的验证属性。 伪代码如下: public interface First{ } public interface Second{ } public class UserModel { @NotNull(message = "{id.empty}", groups = { First.class }) private int id; @NotNull(message = "{username.empty}", groups = { First.class, Second.class }) private String username; @NotNull(message = "{content.empty}", groups = { First.class, Second.class }) private String content; } public String save(@Validated( { Second.class }) UserModel userModel, BindingResult result) { if (result.hasErrors) { return "validate/error"; } return "redirect:/success"; } 对一个参数需要多种验证方式时,也可通过分配不同的组达到目的。例: @NotEmpty(groups = { First.class }) @Size(min = 3, max = 8, groups = { Second.class }) private String name; 分组还支持组序列 默认情况下,不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却很重要,如下面两个例子:(1)第二个组中的约束验证依赖于一个稳定状态来运行,而这个稳定状态是由第一个组来进行验证的。(2)某个组的验证比较耗时,CPU 和内存的使用率相对比较大,最优的选择是将其放在最后进行验证。因此,在进行组验证的时候尚需提供一种有序的验证方式,这就提出了组序列的概念。 一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。 public interface GroupA { } public interface GroupB { } @GroupSequence( { GroupA.class, GroupB.class }) public interface Group { } public @ResponseBody String addPeople(@Validated({Group.class}) People p,BindingResult result) { if(result.hasErrors) { return "0"; } return "1"; } @Valid:作为标准JSR-303规范,还没有吸收分组的功能。 2.注解地方 @Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上 @Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上 两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能。 3.嵌套验证 在比较两者嵌套验证时,先说明下什么叫做嵌套验证。 比如我们现在有个实体叫做Item: public class Item { @NotNull(message = "id不能为空") @Min(value = 1, message = "id必须为正整数") private Long id; @NotNull(message = "props不能为空") @Size(min = 1, message = "至少要有一个属性") private List<Prop> props; } Item带有很多属性,属性里面有:pid、vid、pidName和vidName,如下所示: public class Prop { @NotNull(message = "pid不能为空") @Min(value = 1, message = "pid必须为正整数") private Long pid; @NotNull(message = "vid不能为空") @Min(value = 1, message = "vid必须为正整数") private Long vid; @NotBlank(message = "pidName不能为空") private String pidName; @NotBlank(message = "vidName不能为空") private String vidName; } 属性这个实体也有自己的验证机制,比如pid和vid不能为空,pidName和vidName不能为空等。 现在我们有个ItemController接受一个Item的入参,想要对Item进行验证,如下所示: @RestController public class ItemController { @RequestMapping("/item/add") public void addItem(@Validated Item item, BindingResult bindingResult) { doSomething; } } 在上图中,如果Item实体的props属性不额外加注释,只有@NotNull和@Size,无论入参采用@Validated还是@Valid验证,Spring Validation框架只会对Item的id和props做非空和数量验证,不会对props字段里的Prop实体进行字段验证,也就是@Validated和@Valid加在方法参数前,都不会自动对参数进行嵌套验证。也就是说如果传的List中有Prop的pid为空或者是负数,入参验证不会检测出来。 为了能够进行嵌套验证,必须手动在Item实体的props字段上明确指出这个字段里面的实体也要进行验证。由于@Validated不能用在成员属性(字段)上,但是@Valid能加在成员属性(字段)上,而且@Valid类注解上也说明了它支持嵌套验证功能,那么我们能够推断出:@Valid加在方法参数时并不能够自动进行嵌套验证,而是用在需要嵌套验证类的相应字段上,来配合方法参数上@Validated或@Valid来进行嵌套验证。 我们修改Item类如下所示: public class Item { @NotNull(message = "id不能为空") @Min(value = 1, message = "id必须为正整数") private Long id; @Valid // 嵌套验证必须用@Valid @NotNull(message = "props不能为空") @Size(min = 1, message = "props至少要有一个自定义属性") private List<Prop> props; } 然后我们在ItemController的addItem函数上再使用@Validated或者@Valid,就能对Item的入参进行嵌套验证。此时Item里面的props如果含有Prop的相应字段为空的情况,Spring Validation框架就会检测出来,bindingResult就会记录相应的错误。 总结一下@Validated和@Valid在嵌套验证功能上的区别: