本文主要是介绍Python实现无痛修改第三方库源码的方法详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择...
需求不符合
很多时候,我们下载的 第三方库 是不会有需求不满足的情况,但也有极少的情况,第三方库 没有兼顾到需求,导致开发者无法实现相关功能。
如何通过一些操作将 第三方库 源码进行修改,是我们将要遇到的一个难点。接下来,本文将介绍几个修改源码的操作,看看你有实现过几个?
本文可操作的是 有源码的第三方库,非源码的不在本文讨论范围内。
模拟示例
# -*- coding: uhttp://www.chinasem.cntf-8 -*- import threading import time class Proxy: def __init__(self): # 这个线程是为了模拟网络代理抓包后的发送任务,是测试用的 self.simulate_thread = threading.Thread(target=self.run, args=()) self.simulate_thread.start() self.lock = threading.Lock() self.target = None def run(self): while True: time.sleep(1) with self.lock: if self.target is not None: self.target(self.parse(None)) def parse(self, data): ''' 模拟 解析二进制数据并转为字典 :param data: :return: ''' result = { 'host': '127.0.0.1', 'content_type': 'text/html', 'body': '<html></html>' } return result['body'] def hook(self, target): ''' 模拟挂载方法 :param target: :return: ''' with self.lock: self.target = target
上面代码将模拟一个网络代理,我们将其取名为 Proxy 库,这个网络代理可以捕获 接口二进制数据 ,并返回一个 内容 给开发者。
该网络代理的作者虽然得到了一个比较全的数据,但只返回了 body 给使用者,而现在我们需要获取 host 的内容,所以要进行修改源码来获取。
下面是我们调用的代码:
def get_hook_data(data): print(data) p = Proxy() p.hook(target=get_hook_data)
结果返回:
<html></html>
<html></html>
1. 修改源文件
这个方法应该是绝大部分开发者能想到的办法,由于 python 的第三方库绝大部分都是通过 pip 来安装的,我们可以通过找到 安装路径 的第三方库源码来修改。
例如我们假设上面的 Proxy 的源码安装在了 D:\Env\Project\Lib\site-packages\Proxy ,找到了源码文件 Proxy.py。
源码路径
将源码的 parse() 方法直接进行修改:
def parse(self, data): ''' 模拟 解析二进制数据并转为字典 :param data: :return: ''' result = { 'host': '127.0.0.1', 'content_type': 'text/html', 'body': '<html></html>' } return { 'body': result['body'], 'host': result['host'] }
现在我们来看看返回结果:
{'body': '<html></html>', 'host': '127.0.0.1'}
{'body': '<html></html>', 'host': '127.0.0.1'}
{'body': '<html></html>', 'host': '127.0.0.1'}
{'body': '<html></html>', 'host': '127.0.0.1'}
优点 :简洁明了,非常直接
缺点 :当我们环境发生改变时,每次都需要修改源码,非常麻烦
2. 继承修改
继承修改 的方法比较适合大神,为什么这么说呢?假如我们的这个 二进制数据 解析方法非常非常麻烦,没有一定的了解很难解析,那么这个方法将会非常痛苦。
class MyProxy(Proxy): def parse(self, data): # 这里需要我们自己重新实现第三方库的逻辑 result = { 'host': '127.0.0.1', 'content_type': 'text/html', 'body': '<html></html>' } return { 'body': result['body'], 'host': result['host'] }
我们继承了原来 第三方库 的 类 ,然后通过继承覆写来修改方法的返回值,现在我们可以通过调用 继承 类来实现需求:
def get_hook_data(data): print(data) p = MyProxy() p.hook(target=get_hook_data)
返回结果:
{'body': '<html></html>', 'host': '127.0.0.1'}
{'body': '<html></html>', 'host': '127.0.0.1http://www.chinasem.cn'}
优点 :不需要修改源码文件
缺点 :当源码逻辑非常复杂时,重新去实现逻辑比较困难;如果源码中存在大量调用其他模块的,需要一模一样 import 过来,工作量比较大
额外提供一个方法来减少 继承 实现难度:我们可以通过复制 源码 文件原有逻辑来进行继承,这样会减少很多工作量。
3. 猴子补丁
猴子补丁可以在运行时修改类,通过它我们也可以改写方法,但和继承类似,通过它进行修改也免不了重新实现源码逻辑:
def my_parse(self, data): # 这里需要我们自己重新实现第三方库的逻辑 result = { 'host': '127.0.0.1', 'content_type': 'text/html', 'body': '<html></html>' } return { 'body': result['body'], 'host': result['host'] } Proxy.parse = my_parse
正常调用:
p = Proxy() p.hook(target=get_hook_data)
返回结果:
{'body': '<html></html>', 'host': '127.0.0.1'}
{'body': '<html></html>', 'host': '127.0.0.1'}
优点 :不需要修改源码文件
缺点 :缺点和 继承修改 类似
4. 追踪局部变量
接下来,我们将需要一点 黑魔法 来实现。
众所周知在 PyCharm 进行断点运行时,可以在断点处来获取 局部和全局变量,那么我们是否可以用代码来做到这一点呢?
答案是可以,请看代码:
import sys class VariableTracer: def__init__(self): # 用来保存局部变量 self.vars = None def trace(self, func, *args, **kwargs): old_profile = sys.getprofile() # 设置新的 profiling 函数为我们自定义函数 sys.setprofile(self.profiling) # 调用需要监听的函数 func(*args, **kwargs) # 将以前的 profiling 函数 更换回去 sys.setprofile(old_profile) returnself.vars def profiling(self, frame, event, arg): # 当方法调用 return 之前的局部变量 if event == 'return': vars: dict = frame.f_locals # 保存下来进行返回 self.vars = {key: value for key, value invars.items()} class MyProxy(Proxy): def parse(self, data): vars = VariableTracer().trace(super(MyProxyChina编程, self).parse, data) result = vars['result'] return { 'host': result['host'], 'body': result['body'] }
我们通过 sys.setprofile() 来设置一个自定义的 profiling函数,这个函数在以下事件发生时都会被解释器调用:
函数调用(call):当一个函数被调python用时。
函数返回(return):当一个函数返回时。
异常抛出(exception):当一个异常被抛出时。
C 函数调用(c_call):当一个 C 函数被调用时(仅适用于某些情况)。
我们通过被调用的时机去获取局部变量,这样就可以更换返回值结果。
我们使用自定义类正常调用:
def get_hook_data(data): print(f'hook {data}') p = MyProxy() p.hook(target=get_hook_data)
返回结果:
{'host': '127.0.0.1', 'body': '<html></html>'}
{'host': '127.0.0.1', 'body': '<html></html>'}
优点 :不需要修改源码js文件和重复实现源码逻辑
缺点 :如果源码耗时复杂,可能会有性能问题
结尾
修改源码文件逻辑的事情可能发生的频率不是很高,但真正遇到时那就非常糟心,本文使用了四种方式,如果你还有更好的方式请留言告诉我吧。
到此这篇关于Python实现无痛修改第三方库源码的方法详解的文章就介绍到这了,更多相关Python修改第三方库源码内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!
这篇关于Python实现无痛修改第三方库源码的方法详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!