Python實(shí)現(xiàn)無痛修改第三方庫源碼的方法詳解
需求不符合
很多時(shí)候,我們下載的 第三方庫 是不會有需求不滿足的情況,但也有極少的情況,第三方庫 沒有兼顧到需求,導(dǎo)致開發(fā)者無法實(shí)現(xiàn)相關(guān)功能。
如何通過一些操作將 第三方庫 源碼進(jìn)行修改,是我們將要遇到的一個(gè)難點(diǎn)。接下來,本文將介紹幾個(gè)修改源碼的操作,看看你有實(shí)現(xiàn)過幾個(gè)?
本文可操作的是 有源碼的第三方庫,非源碼的不在本文討論范圍內(nèi)。
模擬示例
# -*- coding: utf-8 -*-
import threading
import time
class Proxy:
def __init__(self):
# 這個(gè)線程是為了模擬網(wǎng)絡(luò)代理抓包后的發(fā)送任務(wù),是測試用的
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):
'''
模擬 解析二進(jìn)制數(shù)據(jù)并轉(zhuǎn)為字典
: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上面代碼將模擬一個(gè)網(wǎng)絡(luò)代理,我們將其取名為 Proxy 庫,這個(gè)網(wǎng)絡(luò)代理可以捕獲 接口二進(jìn)制數(shù)據(jù) ,并返回一個(gè) 內(nèi)容 給開發(fā)者。
該網(wǎng)絡(luò)代理的作者雖然得到了一個(gè)比較全的數(shù)據(jù),但只返回了 body 給使用者,而現(xiàn)在我們需要獲取 host 的內(nèi)容,所以要進(jìn)行修改源碼來獲取。
下面是我們調(diào)用的代碼:
def get_hook_data(data):
print(data)
p = Proxy()
p.hook(target=get_hook_data)
結(jié)果返回:
<html></html>
<html></html>
1. 修改源文件
這個(gè)方法應(yīng)該是絕大部分開發(fā)者能想到的辦法,由于 python 的第三方庫絕大部分都是通過 pip 來安裝的,我們可以通過找到 安裝路徑 的第三方庫源碼來修改。
例如我們假設(shè)上面的 Proxy 的源碼安裝在了 D:\Env\Project\Lib\site-packages\Proxy ,找到了源碼文件 Proxy.py。

源碼路徑
將源碼的 parse() 方法直接進(jìn)行修改:
def parse(self, data):
'''
模擬 解析二進(jìn)制數(shù)據(jù)并轉(zhuǎn)為字典
:param data:
:return:
'''
result = {
'host': '127.0.0.1',
'content_type': 'text/html',
'body': '<html></html>'
}
return {
'body': result['body'],
'host': result['host']
}現(xiàn)在我們來看看返回結(jié)果:
{'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'}
優(yōu)點(diǎn) :簡潔明了,非常直接
缺點(diǎn) :當(dāng)我們環(huán)境發(fā)生改變時(shí),每次都需要修改源碼,非常麻煩
2. 繼承修改
繼承修改 的方法比較適合大神,為什么這么說呢?假如我們的這個(gè) 二進(jìn)制數(shù)據(jù) 解析方法非常非常麻煩,沒有一定的了解很難解析,那么這個(gè)方法將會非常痛苦。
class MyProxy(Proxy):
def parse(self, data):
# 這里需要我們自己重新實(shí)現(xiàn)第三方庫的邏輯
result = {
'host': '127.0.0.1',
'content_type': 'text/html',
'body': '<html></html>'
}
return {
'body': result['body'],
'host': result['host']
}
我們繼承了原來 第三方庫 的 類 ,然后通過繼承覆寫來修改方法的返回值,現(xiàn)在我們可以通過調(diào)用 繼承 類來實(shí)現(xiàn)需求:
def get_hook_data(data):
print(data)
p = MyProxy()
p.hook(target=get_hook_data)
返回結(jié)果:
{'body': '<html></html>', 'host': '127.0.0.1'}
{'body': '<html></html>', 'host': '127.0.0.1'}
優(yōu)點(diǎn) :不需要修改源碼文件
缺點(diǎn) :當(dāng)源碼邏輯非常復(fù)雜時(shí),重新去實(shí)現(xiàn)邏輯比較困難;如果源碼中存在大量調(diào)用其他模塊的,需要一模一樣 import 過來,工作量比較大
額外提供一個(gè)方法來減少 繼承 實(shí)現(xiàn)難度:我們可以通過復(fù)制 源碼 文件原有邏輯來進(jìn)行繼承,這樣會減少很多工作量。
3. 猴子補(bǔ)丁
猴子補(bǔ)丁可以在運(yùn)行時(shí)修改類,通過它我們也可以改寫方法,但和繼承類似,通過它進(jìn)行修改也免不了重新實(shí)現(xiàn)源碼邏輯:
def my_parse(self, data):
# 這里需要我們自己重新實(shí)現(xiàn)第三方庫的邏輯
result = {
'host': '127.0.0.1',
'content_type': 'text/html',
'body': '<html></html>'
}
return {
'body': result['body'],
'host': result['host']
}
Proxy.parse = my_parse
正常調(diào)用:
p = Proxy() p.hook(target=get_hook_data)
返回結(jié)果:
{'body': '<html></html>', 'host': '127.0.0.1'}
{'body': '<html></html>', 'host': '127.0.0.1'}
優(yōu)點(diǎn) :不需要修改源碼文件
缺點(diǎn) :缺點(diǎn)和 繼承修改 類似
4. 追蹤局部變量
接下來,我們將需要一點(diǎn) 黑魔法 來實(shí)現(xiàn)。
眾所周知在 PyCharm 進(jìn)行斷點(diǎn)運(yùn)行時(shí),可以在斷點(diǎn)處來獲取 局部和全局變量,那么我們是否可以用代碼來做到這一點(diǎn)呢?
答案是可以,請看代碼:
import sys
class VariableTracer:
def__init__(self):
# 用來保存局部變量
self.vars = None
def trace(self, func, *args, **kwargs):
old_profile = sys.getprofile()
# 設(shè)置新的 profiling 函數(shù)為我們自定義函數(shù)
sys.setprofile(self.profiling)
# 調(diào)用需要監(jiān)聽的函數(shù)
func(*args, **kwargs)
# 將以前的 profiling 函數(shù) 更換回去
sys.setprofile(old_profile)
returnself.vars
def profiling(self, frame, event, arg):
# 當(dāng)方法調(diào)用 return 之前的局部變量
if event == 'return':
vars: dict = frame.f_locals
# 保存下來進(jìn)行返回
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() 來設(shè)置一個(gè)自定義的 profiling函數(shù),這個(gè)函數(shù)在以下事件發(fā)生時(shí)都會被解釋器調(diào)用:
函數(shù)調(diào)用(call):當(dāng)一個(gè)函數(shù)被調(diào)用時(shí)。
函數(shù)返回(return):當(dāng)一個(gè)函數(shù)返回時(shí)。
異常拋出(exception):當(dāng)一個(gè)異常被拋出時(shí)。
C 函數(shù)調(diào)用(c_call):當(dāng)一個(gè) C 函數(shù)被調(diào)用時(shí)(僅適用于某些情況)。
我們通過被調(diào)用的時(shí)機(jī)去獲取局部變量,這樣就可以更換返回值結(jié)果。
我們使用自定義類正常調(diào)用:
def get_hook_data(data):
print(f'hook {data}')
p = MyProxy()
p.hook(target=get_hook_data)
返回結(jié)果:
{'host': '127.0.0.1', 'body': '<html></html>'}
{'host': '127.0.0.1', 'body': '<html></html>'}
優(yōu)點(diǎn) :不需要修改源碼文件和重復(fù)實(shí)現(xiàn)源碼邏輯
缺點(diǎn) :如果源碼耗時(shí)復(fù)雜,可能會有性能問題
結(jié)尾
修改源碼文件邏輯的事情可能發(fā)生的頻率不是很高,但真正遇到時(shí)那就非常糟心,本文使用了四種方式,如果你還有更好的方式請留言告訴我吧。
到此這篇關(guān)于Python實(shí)現(xiàn)無痛修改第三方庫源碼的方法詳解的文章就介紹到這了,更多相關(guān)Python修改第三方庫源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python統(tǒng)計(jì)python文件中代碼,注釋及空白對應(yīng)的行數(shù)示例【測試可用】
這篇文章主要介紹了Python統(tǒng)計(jì)python文件中代碼,注釋及空白對應(yīng)的行數(shù),涉及Python針對py文件的讀取、遍歷、判斷、統(tǒng)計(jì)等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
python文件讀取read及readlines兩種方法使用詳解
這篇文章主要為大家介紹了python文件讀取read及readlines兩種方法的使用示例及區(qū)別詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
matplotlib.pyplot.matshow 矩陣可視化實(shí)例
這篇文章主要介紹了matplotlib.pyplot.matshow 矩陣可視化實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06
python控制臺中實(shí)現(xiàn)進(jìn)度條功能
這篇文章主要介紹了python控制臺中實(shí)現(xiàn)進(jìn)度條功能的方法,想要了解的朋友可以參考一下2015-11-11
詳解Python的Django框架中的模版相關(guān)知識
這篇文章主要介紹了Python的Django框架中的模版相關(guān)知識,模版的存在大大簡化了創(chuàng)作頁面時(shí)HTML的相關(guān)工作,需要的朋友可以參考下2015-07-07
教你用 Python 實(shí)現(xiàn)微信跳一跳(Mac+iOS版)
這幾天看網(wǎng)上好多微信跳一跳破解了,不過都是安卓的,無奈蘋果不是開源也沒辦法。本文給大家分享用 Python 來玩微信跳一跳(Mac+iOS版),具體實(shí)現(xiàn)代碼大家參考下本文2018-01-01
在自動化中用python實(shí)現(xiàn)鍵盤操作的方法詳解
今天小編就為大家分享一篇在自動化中用python實(shí)現(xiàn)鍵盤操作的方法詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07

