什么是hook?
钩子函数,顾名思义,就是把我们自己实现的自定义函数在某一时刻挂接到目标挂载点上去执行。
-
hook函数,就是我们自己实现的函数,函数类型与挂载点匹配(返回值,参数列表)
-
挂接,也就是hook或者叫注册(register),使得hook函数对目标可用
-
目标挂载点,也就是挂我们hook函数的地方(我们想在这个目标点实现我们自己的功能)
hook是一种编程机制,与具体的编程语言无关。
为什么需要hook?
什么情况下需要实现hook?
---就是一个功能(类/方法)自身无法满足所有需求,那么可以通过hook 就提供扩展自身能力的可能
同一个需求平替实现
比如有个需求:获取每次请求后的请求状态码
方式1:原始做法直接在代码里增加
--不推荐,而且违反"开闭原则"
python
import requests
url = ' http://www.baidu.com'
r = requests.get(url)
print(f"status doce: {r.status_code}")
方式2:使用装饰器
python
import requests
# 定义装饰器
def print_status_code(func):
def wrappers(*args, **kwargs):
res = func(*args, **kwargs)
print(f"Status code: {res.status_code}")
return res
return wrappers
@print_status_code
def send_request(url):
res = requests.get(url)
return res
url='https://httpbin.org/get'
send_request(url)
方式3:使用requests库自动hooks入参来使用钩子函数:将"获取请求状态的逻辑封装到一个函数中,在执行完请求后,就自动执行钩子函数"
python
import requests 没有参数
def custom_hooks(response,**kwargs):
print(response.status_code)
def custom_hooks2(response,**kwargs):
print(response.headers)
url = ' http://www.baidu.com'
response = requests.get(url, hooks={"response": [custom_hooks, custom_hooks2]})
钩子函数什么时候执行?
比如:response = requests.get(url, hooks={"response": [custom_hooks, custom_hooks2]})
自定义的钩子函数在执行完请求后,执行钩子函数
实例:requests库的hooks介绍
requests提供了hook机制,让我们能够在请求得到响应之后,再去额外做一些自定义的操作,比如打印某些信息、修改响应内容等。
注意:
- requests库中钩子函数是在请求得到响应之后才去行的钩子函数
- 如下示例测试数据基于requests 2.25.1
requests库如何使用hooks来调用钩子函数?
python
import requests
# 钩子函数1
def print_url(r, **kwargs):
print("raw_url "+r.url)
# 钩子函数2
def change_url(r, **kwargs):
r.url = ' http://change.url'
print("changed_url "+r.url)
return r # 其实没有这句话,也可以修改r.url,因为r是response对象而非普通数值,但requests官方似乎误认为回调函数一定要有return才能替换传入的数据
url = ' http://httpbin.org/cookies'
# 使用hooks形参来调用钩子函数
response = requests.get(url, hooks=dict(response=[print_url, change_url]))
print("result_url "+response.url)
注意:
- 钩子函数中的**kwargs形参不能少,且必须是关键字参数
□ 因为在调用钩子函数时,requests.session.py实现了添加了相关参数 2) requests.中的hook入参值类型必须是字典,且key必须是"response",value有多个时必须为list类型。
□ key必须是"response": 因为
①requests.hooks.py如下代码有校验
②requests.models.py有如下校验
requests库使用钩子函数时,如果钩子函数需要传递参数,如何向钩子函数传递参数?
有两种方式:
方式1 : 使用偏函数(funtools.partial)过要注意,使用这种方式,不能适用于钩子函数有多个位置参数的情况,只适配与钩子函数有关键字参数的情况。所以要将钩子函数设计成def my_hook_func(response, **kwargs)这种形式。
因为当使用 functools.partial 结合 requests 库的钩子函数时,直接传递位置参数会干扰 requests 默认传递给钩子函数的参数顺序。 requests 库在调用钩子函数时,会将 response 对象作为第一个参数传入。如果使用 partial 传递位置参数,这些位置参数会占用钩子函数本来为 response 留下的位置,从而导致错误。
示例:
python
import requests
from functools import partial
# 定义钩子函数
def response_hook(response, **kwargs):
print(f"所有关键字参数{kwargs}")
parms1 = kwargs.get('parms1')
parms2 = kwargs.get('parms2')
print("额外参数2:", parms1)
print("额外参数2:", parms2)
print("响应状态码:", response.status_code)
return response
# 使用 partial 来创建一个新的钩子函数,其中传入了关键字参数
hook_with_param = partial(response_hook, parms1='val1', parms2='val2')
# 进行请求,并传入钩子函数
response = requests.get('https://httpbin.org/get', hooks={'response': hook_with_param})
# 打印响应内容
print(response.text)
方式2 :使用闭包
使用闭包比使用偏函数稍微麻烦一点,不过使用闭包能支持位置参数和关键字参数,这点要比偏函数好
python
import requests
# 定义一个外层函数来包裹钩子函数,从而传入额外的参数
def create_response_hook(extra_param, additional_arg, **kwargs2):
def response_hook(response, *args, **kwargs):
print(f"kwargs参数的值{kwargs}")
print("响应状态码:", response.status_code)
print("额外参数:", extra_param)
print("额外位置参数:", additional_arg)
print("额外关键字参数:", kwargs2)
print("params1参数:", kwargs2.get("params1"))
print("params2参数:", kwargs2.get("params2"))
return response
return response_hook
# 创建带有额外参数的钩子函数
hook_with_param = create_response_hook('额外的数据', '额外位置参数1', params1="val1", params2="val2")
# 进行请求,并传入钩子函数
response = requests.get('https://httpbin.org/get', hooks={'response': hook_with_param})
# 打印响应内容
print(response.text)