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

news2025/5/20 4:07:38

需求不符合

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

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

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

模拟示例 

# -*- coding: utf-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.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 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(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.coloradmin.cn/o/2325090.html

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

相关文章

一区严选!挑战5天一篇脂质体组学 DAY1-5

Day 1! 前期已经成功挑战了很多期NHANES啦&#xff01;打算来试试孟德尔随机化领域&#xff5e; 随着孟德尔随机化研究的普及&#xff0c;现在孟德尔发文的难度越来越高&#xff0c;简单的双样本想被接收更是难上加难&#xff0c;那么如何破除这个困境&#xff0c;这次我打算…

自学-408-《计算机网络》(总结速览)

文章目录 第一章 计算机网络概述1. 计算机网络的定义2. 计算机网络的基本功能3. 计算机网络的分类4. 计算机网络的层次结构5. 计算机网络的协议6. 计算机网络的组成部分7. 计算机网络的应用8. 互联网的概念 物理层的主要功能第二章 数据链路层和局域网1. 数据链路层的功能2. 局…

【质量管理】纠正、纠正措施和预防的区别与解决问题的四重境界

“质量的定义就是符合要求”&#xff0c;我们在文章【质量管理】人们对于质量的五个错误观念-CSDN博客中提到过&#xff0c;这也是质量大师克劳士比所说的。“质量的系统就是预防”&#xff0c;防止出现产品不良而造成的质量损失。 质量问题的解决可以从微观和宏观两个方面来考…

新手SEO优化实战快速入门

内容概要 对于SEO新手而言&#xff0c;系统化掌握基础逻辑与实操路径是快速入门的关键。本指南以站内优化为切入点&#xff0c;从网站结构、URL设计到内链布局&#xff0c;逐层拆解搜索引擎友好的技术框架&#xff1b;同时聚焦关键词挖掘与内容策略&#xff0c;结合工具使用与…

sqli-labs靶场 less 11

文章目录 sqli-labs靶场less 11 POS联合注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&…

tomcat部署项目打开是404?

问题描述 今天在帮助一个小伙伴解决问题的时候 部署成功了 就是打不开总是404 他这个项目是公司的一个18年的项目 巨老&#xff01;&#xff01;&#xff01; HTTP状态 404 - 未找到 类型 状态报告 描述 源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示…

[Linux]解决虚拟机 ubantu系统下网络的问题

问题来源&#xff1a;打开ubantu发现网络连接标识消失 解决步骤&#xff1a; 重新安装&#xff0c;前面操作无效 切换桥接模式、直连手机网络 已解决&#xff1a;

如何使用stable diffusion 3获得最佳效果

参考&#xff1a;How to get the best results from Stable Diffusion 3 Scaling Rectified Flow Transformers for High-Resolution Image Synthesis prompting SD3 不再受限于CLIP的最长77个token的长度限制&#xff0c;可以输入更长的prompt。 &#xff08;两个CLIP模型的…

Java学习笔记1——编程基础

一、整数类型变量 注意&#xff1a;每个字符型常量占两个字节 二、自动类型转换和强制类型转换 ​​​三、算术运算符 四、赋值运算符 五、比较运算符 六、逻辑运算符 七、运算符的优先级 运算符的优先级可以通过以下口诀来记忆&#xff1a; 括号优先&#xff0c;单目次之&am…

微服务核心知识点深度解析:从组件到架构设计

微服务核心知识点深度解析&#xff1a;从组件到架构设计 微服务核心知识点深度解析&#xff1a;从组件到架构设计一、Spring Cloud 5 大核心组件详解二、服务注册与发现&#xff1a;微服务的 “通讯录”概念解析Spring Cloud 中的实现 三、Nacos&#xff1a;不止是注册中心核心…

SpringBoot3+EasyExcel通过WriteHandler动态实现表头重命名

方案简介 为了通过 EasyExcel 实现动态表头重命名&#xff0c;可以封装一个方法&#xff0c;传入动态的新表头名称列表&#xff08;List<String>&#xff09;&#xff0c;并结合 WriteHandler 接口来重命名表头。同时&#xff0c;通过 EasyExcel 将数据直接写入到输出流…

Python小练习系列 Vol.11:回文数筛选(filter + 字符串反转)

&#x1f9e0; Python小练习系列 Vol.11&#xff1a;回文数筛选&#xff08;filter 字符串反转&#xff09; &#x1f50d; 本期我们用 Python 的 filter() 函数结合字符串反转技巧&#xff0c;一行代码搞定“回文数”的判断与筛选&#xff01; &#x1f9e9; 一、题目描述 回…

BUUCTF-web刷题篇(5)

13.upload1 文件上传漏洞&#xff08;上传图片&#xff09; 按照传统方法&#xff0c;新建文件&#xff08;xinjian&#xff09;写一句话木马&#xff0c;利用Windows文件后缀识别的特点&#xff0c;将后缀名改为图片后缀名(xinjian.jpg)&#xff0c;上传文件&#xff0c;抓包…

NestJS——创建项目、编写User模块

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

《Python Web部署应知应会》No2:如何基于FastAPI 和 OLLAMA 架构实现高并发 AI 推理服务

《Python Web部署应知应会》No2&#xff1a;如何基于FastAPI 和 OLLAMA 架构实现高并发 AI 推理服务&#xff08;上&#xff09; 摘要&#xff1a; 在 FastAPI 和 OLLAMA 架构中实现高并发 AI 推理服务&#xff0c;并优化性能指标采集和缓存策略&#xff0c;可以充分利用 asy…

NUUO摄像头debugging_center_utils命令执行漏洞

免责声明&#xff1a;本号提供的网络安全信息仅供参考&#xff0c;不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权&#xff0c;请及时与我联系&#xff0c;我将尽快处理并删除相关内容。 漏洞描述 NUUO NVR是中国台湾省NUUO公司旗…

2.pycharm部署Ai - 编程好助手

一、pycharm安装continue插件 1.提前安装好pycharm&#xff0c;并双击打开 2.File – Setting 3.Plugins – 搜索Continue &#xff0c; 点击Install安装 4.点ok 二、获取硅基流动API 1.登入网站&#xff1a;https://siliconflow.cn/zh-cn/#/&#xff0c;并注册登入 2.获取AP…

uniapp + Axios + 小程序封装网络请求

前言 小程序自带的网络请求使用起来比较麻烦&#xff0c;不便于管理&#xff0c;就需要封装网络请求&#xff0c;减少繁琐步骤&#xff0c;封装最终效果&#xff0c;根据类别将网络请求封装在文件中&#xff0c;使用得时候调用文件名名称加文件中得自定义名称&#xff0c;就可…

初始数据库--MySQL

一&#xff0c;数据库&#xff08;DataBase&#xff09;概述 思考程序开发中数据存储问题&#xff1f; 变量 int c 10; 对象{名字&#xff0c;价格&#xff0c;颜色} 集合类-->对象 以上方式存储数据都是在内存中存储 程序运行结束后数据都释放了 …

ENSP学习day12

链路聚合–LACP&#xff08;Link Aggregation Control Protocol&#xff09; 链路聚合是指将多条物理链路通过一种技术进行绑定&#xff0c;从而形成一个逻辑上更高带宽、更可靠的链路。这种技术能够提高网络传输速率和可靠性&#xff0c;同时实现负载均衡。链路聚合可以在不同…