python黑科技:无痛修改第三方库源码

需求不符合

很多时候,我们下载的 第三方库 是不会有需求不满足的情况,但也有极少的情况,第三方库 没有兼顾到需求,导致开发者无法实现相关功能。

如何通过一些操作将 第三方库 源码进行修改,是我们将要遇到的一个难点。接下来,本文将介绍几个修改源码的操作,看看你有实现过几个?

本文可操作的是 有源码的第三方库,非源码的不在本文讨论范围内。

模拟示例 

# -*- coding: utf-8 -*-
import threading
import timeclass Proxy:def __init__(self):# 这个线程是为了模拟网络代理抓包后的发送任务,是测试用的self.simulate_thread = threading.Thread(target=self.run, args=())self.simulate_thread.start()self.lock = threading.Lock()self.target = Nonedef 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.1'}
  • 优点 :不需要修改源码文件

  • 缺点 :当源码逻辑非常复杂时,重新去实现逻辑比较困难;如果源码中存在大量调用其他模块的,需要一模一样 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 sysclass VariableTracer:def__init__(self):# 用来保存局部变量self.vars = Nonedef trace(self, func, *args, **kwargs):old_profile = sys.getprofile()# 设置新的 profiling 函数为我们自定义函数sys.setprofile(self.profiling)# 调用需要监听的函数func(*args, **kwargs)# 将以前的 profiling 函数 更换回去sys.setprofile(old_profile)returnself.varsdef 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(MyProxy, self).parse, data)result = vars['result']return {'host': result['host'],'body': result['body']}

我们通过 sys.setprofile() 来设置一个自定义的 profiling函数,这个函数在以下事件发生时都会被解释器调用:

  1. 函数调用(call):当一个函数被调用时。

  2. 函数返回(return):当一个函数返回时。

  3. 异常抛出(exception):当一个异常被抛出时。

  4. 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>'}
  • 优点 :不需要修改源码文件和重复实现源码逻辑

  • 缺点 :如果源码耗时复杂,可能会有性能问题

结尾

修改源码文件逻辑的事情可能发生的频率不是很高,但真正遇到时那就非常糟心,本文使用了四种方式,如果你还有更好的方式请留言告诉我吧。

如果这篇文章对你有帮助,点个赞让我知道哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十三章:优化内存管理_《C++性能优化指南》_notes

优化内存管理 一、内存管理基础概念二、自定义分配器三、智能指针优化重点知识代码示例&#xff1a;智能指针性能对比 四、性能优化关键点总结多选题设计题答案与详解多选题答案设计题示例答案&#xff08;第1题&#xff09; 一、内存管理基础概念 重点知识 动态内存分配开销…

python笔记之函数

函数初探 python在要写出函数很简单&#xff0c;通过关键字def即可写出&#xff0c;简单示例如下 def add(a, b):return ab 以上即可以定义出一个简单的函数&#xff1a;接收两个变量a和b&#xff0c;返回a和b相加的结果&#xff0c;当然这么说也不全对&#xff0c;原因就是…

【服务器操作指南 - GPU 使用与文件传输】轻松掌握 GPU 状态查看和服务器文件传输技巧

0. 引言 在使用服务器时&#xff0c;高效管理 GPU 和文件传输是两项不可或缺的技能。 本指南旨在帮助您快速掌握服务器环境下的 GPU 使用状态监测方法&#xff0c;并简要介绍如何在服务器之间进行文件传输操作。 1. 查看服务器上的 gpu 使用状态 1.1 安装 gpustat 这条指令…

0330-YYYY-MM-DD格式日期比较大小

最简单的&#xff08;python&#xff09; from datetime import datetime def compare_time(time1,time2): time1_t datetime.strptime(time1,“%Y-%m-%d”) time2_t datetime.strptime(time2,“%Y-%m-%d”) if time1_t < time2_t: return time1_t elif time1_t > ti…

QFlightInstruments飞行仪表控件库

QFlightInstruments 是一个开源的飞行仪表控件库&#xff0c;专为基于 Qt 的应用程序设计。它提供了一系列仿真实飞机仪表的组件&#xff0c;适用于飞行模拟软件、航空电子系统或任何需要高仿真飞行仪表显示的项目。 主要功能 高仿真飞行仪表&#xff1a;包括空速表、高度表、…

VSCode 市场发现恶意扩展正在传播勒索软件!

在VSCode 市场中发现了两个隐藏着勒索软件的恶意扩展。其中一个于去年 10 月出现在微软商店&#xff0c;但很长时间没有引起注意。 这些是扩展ahban.shiba 和 ahban.cychelloworld&#xff0c;目前已从商店中删除。 此外&#xff0c;ahban.cychelloworld 扩展于 2024 年 10 月…

国信华源携AI+水利创新成果亮相第十五届防汛抗旱信息化技术交流会

直击展会现场 近日&#xff0c;以“人工智能赋能防汛抗旱 融合创新共御极端灾害”为主题的第十五届防汛抗旱信息化技术交流会在河南郑州召开。作为水旱灾害防御领域的专精企业&#xff0c;北京国信华源科技有限公司携自主研发的入户叫应预警系统及覆盖防汛抗旱全链条的智慧化场…

MATLAB语言的链表反转

MATLAB语言的链表反转 链表是一种常见的数据结构&#xff0c;与数组相比&#xff0c;链表在插入和删除操作方面具有更高的灵活性。然而&#xff0c;链表的一些操作&#xff0c;比如反转链表&#xff0c;对一些初学者来说可能是一个挑战。本篇文章将重点讨论如何使用MATLAB语言…

Oracle数据库数据编程SQL<2.2 DDL 视图、序列>

目录 一、Oracle 视图(Views) &#xff08;一&#xff09; Oracle 视图特点 &#xff08;二&#xff09;Oracle 视图创建语法 关键参数&#xff1a; &#xff08;三&#xff09;Oracle 视图类型 1、普通视图 2、连接视图&#xff08;可更新&#xff09; 3、对象视图 4…

QtAdvancedStylesheets使用

QtAdvancedStylesheets 是一个基于 Qt Widgets 的样式表(QSS)增强库,允许开发者通过类似 CSS 的方式深度定制 Qt 应用程序的界面风格,支持动态主题切换、动画效果和复杂控件样式设计。 1. 核心功能 高级样式表支持 使用 CSS-like 语法美化 Qt Widgets(如 QPushButton、Q…

QtAV入门

QtAV 是一个基于 FFmpeg 和 Qt 的高性能多媒体播放框架,提供强大的音视频解码、渲染和处理能力,适合开发跨平台的播放器、视频编辑和流媒体应用。 1. 核心功能 多格式支持 支持 H.264/H.265、VP9、AV1 等视频编码。 支持 MP3、AAC、Opus 等音频编码。 封装格式:MP4、MKV、…

[ C++ ] | C++11 从左值引用到右值引用

&#xff08;目录占位&#xff09; 1. 前言&#xff1a; C 11 是在 C 98 之后又一个变化比较大的标准。为C增加了很多东西&#xff0c;其中有一部分是有用的&#xff0c;有一部分是我自认为作用不是很大东西。这一章呢&#xff1f;我们就来说说C11我&#xff0c;我认为对性能…

基于MCU实现的电机转速精确控制方案:软件设计与实现

本文将详细介绍一篇基于微控制器&#xff08;MCU&#xff09;的电机转速精确控制的软件方案。通过采样PWM信号控制和ADC采样技术&#xff0c;结合PID闭环控制算法&#xff0c;实现了电机转速的高效、稳定调节。以下是软件方案流程图&#xff0c;下文将对其进行展开讲解。 原图太…

Jmeter触发脚本备份

JMeter 在以下情况会触发脚本备份&#xff1a; 手动保存测试计划时&#xff1a;如果测试计划有未保存的修改&#xff0c;当用户手动保存测试计划&#xff08;脚本&#xff09;时&#xff0c;JMeter 都会自动将当前脚本备份到${JMETER_HOME}/backups文件夹下。 关闭 JMeter 时…

AI人工智能-PyCharm的介绍安装应用

下载与安装 创建python项目 项目路径&#xff1a;C:\Users\miloq\Desktop\python_project 配置环境 提前找到conda配置的python-base路径 配置conda环境 运行项目 运行结果

Flink内存模型--flink1.19.1

Flink 的 JobManager 和 TaskManager 在内存分配上有不同的职责和结构。以下是两者的内存分类及详细说明&#xff1a; 一、JobManager 内存分类 JobManager 主要负责作业调度、协调&#xff08;如 Checkpoint 协调&#xff09;、资源管理等&#xff0c;其内存需求相对较低&…

华为数字化转型-方法篇

1 方法篇-3-愿景驱动的数字化转型规划 1.2 业务战略是数字化转型的龙头 1.3 数字时代&#xff0c;企业需要适时地调整业务战略 1.3.1 引入数字化商业模式 引入数字化商业模式包括改变与客户做生意的方式&#xff0c;改变销售的渠道&#xff0c;基于产业互联网重新定位与行 业…

常用的排序算法------练习4

1. 题目 2. 思路和题解 这道题是很经典的荷兰国旗问题&#xff0c;根据题目意思&#xff0c;要对这个数组按照颜色排序&#xff0c;而此时现在的红、白、蓝三个颜色分别对应0&#xff0c;1&#xff0c;2&#xff0c;因此可以想到使用冒泡排序对该数组进行排序。 代码如下&…

传统神经网络、CNN与RNN

在网络上找了很多关于深度学习的资料&#xff0c;也总结了一点小心得&#xff0c;于是就有了下面这篇文章。这里内容较为简单&#xff0c;适合初学者查看&#xff0c;所以大佬看到这里就可以走了。 话不多说&#xff0c;上图 #mermaid-svg-Z3k5YhiQ2o5AnvZE {font-family:&quo…

1371. 货币系统-dp背包问题

给定 V种货币&#xff08;单位&#xff1a;元&#xff09;&#xff0c;每种货币使用的次数不限。 不同种类的货币&#xff0c;面值可能是相同的。 现在&#xff0c;要你用这 V种货币凑出 N 元钱&#xff0c;请问共有多少种不同的凑法。 输入格式 第一行包含两个整数 V 和 N…