"); //-->
什么是Hook,就是在一個已有的方法上加入一些鉤子,使得在該方法執行前或執行后另在做一些額外的處理,那么Hook技巧有什么作用以及我們為什么需要使用它呢,事實上如果一個項目在設計架構時考慮的足夠充分,模塊抽象的足夠合理,設計之初為以后的擴展預留了足夠的接口,那么我們完全可以不需要Hook技巧。但恰恰架構人員在項目設計之初往往沒辦法想的足夠的深遠,使得后續在擴展時深圳面臨重構的痛苦,這時Hook技巧似乎可以為我們帶來一記緩兵之計,通過對舊的架構進行加鉤子來滿足新的擴展需求。

下面我們就來看看如果進行Hook處理,我們按照Hook的對象的層級來逐一介紹:
對類進行Hook
也就是說我們得鉤子需要監控到類的創建等操作,然后在此之前或之后做我們希望的操作
1、Hook類的創建
你可以在寫一個類的時候為其添加__metaclass__屬性
classFoo(Bar):__metaclass__=something…
Python創建類的過程是這樣的:
Foo中有__metaclass__這個屬性嗎?如果是,Python會在內存中通過__metaclass__創建一個名字為Foo的類。如果Python沒有找到__metaclass__,它會繼續在Bar(父類)中尋找__metaclass__屬性,并嘗試做和前面同樣的操作。如果Python在任何父類中都找不到__metaclass__,它就會在模塊層次中去尋找__metaclass__,并嘗試做同樣的操作。如果還是找不到__metaclass__,Python就會用內置的type來創建這個類對象。
所以我們需要在給__metaclass__屬性的值是一個能夠創建一個類的東西,即一個繼承type的類。
比如:
classSingleton(type):def__init__(cls,name,bases,dict):super(Singleton,cls).__init__(name,bases,dict)cls._instance=Nonedef__call__(cls,*args,**kw):ifcls._instanceisNone:cls._instance=super(Singleton,cls).__call__(*args,**kw)returncls._instanceclassMyClass(object):__metaclass__=Singleton
Singleton就是一個能夠創建類的對象,因為它繼承了type
也正因為此,我們可以在Singleton這個類中去監控MyClass的創建過程
2、Hook實例屬性
這里我們需要操作的屬性是__getattribute__和__getattr__
object.__getattribute__(self,name):無論訪問存在還是不存在的屬性都先訪問該方法
object.__getattr__(self,name):當不存在__getattribute__方法或者引發了AttributeError異常時訪問該方法
classC(object):a='abc'def__getattribute__(self,*args,**kwargs):print(__getattribute__()iscalled)returnobject.__getattribute__(self,*args,**kwargs)def__getattr__(self,name):print(__getattr__()iscalled)returnnamec=C()printc.a__getattribute__()iscalledabcprintc.aa__getattribute__()iscalled__getattr__()iscalledaa
可以看到,訪問已有屬性a時,__getattribute__被調用,訪問未定義的屬性aa時__getattribute__先被調用,接著__getattr__被調用
3、Hook類屬性
python描述符是一個“綁定行為”的對象屬性,在描述符協議中,它可以通過方法重寫屬性的訪問。這些方法有__get__(),__set__(),和__delete__()。如果這些方法中的任何一個被定義在一個對象中,這個對象就是一個描述符。
classDesc(object):def__get__(self,instance,owner):print(__get__...)def__set__(self,instance,value):print('__set__...')classTestDesc(object):x=Desc()t=TestDesc()t.x__get__...
-self:Desc的實例對象,其實就是TestDesc的屬性x
-instance:TestDesc的實例對象,其實就是t
-owner:即誰擁有這些東西,當然是TestDesc這個類,它是最高統治者,其他的一些都是包含在它的內部或者由它生出來的
為了讓描述符能夠正常工作,它們必須定義在類的層次上。否則Python無法自動為你調用__get__和__set__方法。
而根據之前對類方法的說明,引用t.x的時候是否會先引用TestDesc的__getattribute__方法呢?答案是會的,其實訪問屬性時在python中真實的查找順序是這樣的:
1)__getattribute__(),無條件調用
2)數據描述符(定義了__set__或__delete__的描述符):由1)觸發調用(若人為的重載了該__getattribute__()方法,可能會導致無法調用描述符)
3)實例對象的字典
4)類的字典
5)非數據描述符(只定義了__get__的描述符)
6)父類的字典
7)__getattr__()方法
4、使用修飾符來Hook類
defsingleton(cls,*args,**kw):instances={}def_singleton():ifclsnotininstances:instances[cls]=cls(*args,**kw)returninstances[cls]return_singleton@singletonclassMyClass(object):a=1def__init__(self,x=0):self.x=x
我們使用singleton方法把MyClass修飾為了一個單例模式,同時我們也在singleton方法中實現了對MyClass實例過程的監控。
對方法進行Hook
1、修飾符來Hook方法
1)修飾不帶參數的方法
defsomething(func):defwrap():printstartfunc()printendreturnwrap@somethingdeffunc():pass
2)修飾帶參數的方法
defsomething(func):defwrap(*args,**kargv):printstartfunc(*args,**kargv)printendreturnwrap@somethingdeffunc(a,b):passdefsomething(a,b):defnew_func(func):defwrap(*args,**kargv):printafunc(*args,**kargv)printbreturnwrapreturnnew_func@something(1,2)deffunc(a,b):pass
3)使用帶參數的修飾符來修飾方法
其他Hook
1、Hook內建方法
#Hookopen方法real_open=__builtins__.open__builtin__.open=my_open#Hookimport方法real_importer=__import____builtins__.__import__=my_importer
上述操作使得my_open代替了python內置的open方法,故而我們可以使用我們自己的my_open方法來監控后續對open方法的調用了
2、MonkeyPatch
fromSomeOtherProduct.SomeModuleimportSomeClassdefspeak(self):return"ookookeeeeeeeee!"SomeClass.speak=speak
實際上這是所有語言都會使用到的Hook技巧,往往在我們使用了第三方的包,希望在之上做一些擴展,但又不想改動原有的代碼時使用
多說一句
上述提到了修飾符的操作,那么我們在使用修飾符時有一些小技巧需要了解
1、使用functools
防止使用修飾器后函數簽名被改變
fromfunctoolsimportwrapsdefmy_dec(func):@wraps(func)defwrapped():print%siscalled%func.__name__returnfunc()returnwrapped@my_decdeffoo():pass
這樣處理后,foo方法的簽名與被修飾之前保持了一致,否則簽名將會變成my_dec方法的簽名
2、使用decorator模塊來做修飾器
fromdecoratorimportdecorator@decoratordefwrap(f,*args,**kw):
printstartf(*args,**kw)printend#這樣wrap方法就變成了一個decorator@wrapdeffunc():printfunc
3、使用類做修飾器
classtest(object):def__init__(self,func):self._func=funcdef__call__(self):printstartself._func()printend@testdeffunc():printfuncfunc()startfuncend
實際應用中很少遇到可以使用一個類作為修飾器,但實際上只要一個類實現了__call__方法,其就可以作為一個修飾器存在了,并且由于類的可操作性較方法更強大,所以類做修飾器也可以實現更豐富的特性。
最后想要了解更多關于Python發展前景趨勢,請關注扣丁學堂python培訓官網、微信等平臺,扣丁學堂IT職業在線學習教育平臺為您提供最新的Python視頻教程系統,通過千鋒扣丁學堂金牌講師在線錄制的Python視頻教程課程,讓你快速掌握Python從入門到精通開發實戰技能。扣丁學堂Python技術交流群:943406067。
*博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。
相關推薦
模擬精英—與業內專家面對面互聯2
串口讀取任務不執行?
OpenAI o3 模型基準測試成績遭質疑,實測分數遠不及宣稱
國產AP SoC,殺出中低端重圍
模擬精英—與業內專家面對面互聯4
模擬精英—與業內專家面對面互聯6
單片微機控制的全自動交流穩壓電源
EMI噪聲源的分析與優化方法
06.22.04.I-LOGIX_RHAPSODY_V5.01_ISO-LND
555DGK—63L雙功能消毒柜電路圖
555使用臭氧管的電子消毒電路圖
單邊帶調制中的相位法與希爾伯特變換
請教ads8260 pilot板子的調試
技創發布多款ARM/COLDFIRE核心板OEM服務
關稅抵制雙重暴擊,特斯拉cybercab首秀已不能再拖
模擬精英—與業內專家面對面互聯5
如何減少車燈控制器MCU的數量來優化成本
曾幾荷時....
超高效AI模型,在CPU上運行
掌握拓撲選擇:優化電池供電設備設計
模擬精英—與業內專家面對面互聯3
第四屆全國電源技術年會論文集
555蔬菜滅菌解毒機電路圖
555實用電子誘鼠器電路圖
CADDX卡德克斯攜FPV黑科技引爆中國國際模型博覽會
555病人昏倒語言呼救盒電路圖
電磁兼容基礎課程 培訓與范例
單相相位觸發器TC782A的設計及應用
研究:AI 醫療診斷平均準確率 52.1%,與非專家醫生相當
第十三屆全國電源技術年會論文集錦(全系列)