python筆記69 - 什么是猴子補丁(Monkey Patch)?


前言

python里面什么是猴子補丁(Monkey Patch)?,使用場景有哪些?
猴子補丁主要有以下幾個用處:

  • 在運行時替換方法、屬性等
  • 在不修改第三方代碼的情況下增加原來不支持的功能
  • 在運行時為內存中的對象增加patch而不是在磁盤的源代碼中增加

猴子補丁(Monkey Patch)

屬性在運行時的動態替換,叫做猴子補丁(Monkey Patch)。
作用是在運行的時候,動態替換模塊方法。先看一個簡單的例子
如果有一個模塊somemodule.py,在其它代碼里面有用到這個類里面的speak方法

class SomeClass(object):

    def __init__(self):
        self.name = "yoyo"

    def speak(self):
        return "hello world"

在不改變原來代碼的基礎上,可以重新定義一個speck方法,替換原來的

from xxx.somemodule import SomeClass


def new_speak(self):
    return "new hello"

# 替換speck方法
SomeClass.speak = new_speak

替換之后,調用的時候,就會變成新的方法里面內容

some = SomeClass()
print(some.speak())
# 運行結果 new hello

python自定義對象轉json串

我自己定義了一個類,如下

class MyDefined(object):

    def __init__(self):
        self.name = "yoyo"
        self.age = 18

    def __repr__(self):
        return 'name={}&age={}'.format(self.name, self.age)

在定義字典的時候,引用了上面類的實例

aa = {
    "a1": True,
    "b1": "hello",
    "c1": [1, 2, 3],
    "d1": MyDefined()
}
print(aa)
# 運行結果:{'a1': True, 'b1': 'hello', 'c1': [1, 2, 3], 'd1': name=yoyo&age=18}

如果直接轉json串,會出現異常:TypeError: Object of type 'MyDefined' is not JSON serializable

import json

print(json.dumps(aa))

因為MyDefined類是我自己定義的,json庫無法解析成對應的字符串,這種情況就需要自己去寫一個解析方式

方法一:可以在json.dumps 傳一個cls參數

import json

class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, MyDefined):
            return str(obj)
        else:
            return json.JSONEncoder.default(self, obj)
print(json.dumps(aa, cls=MyEncoder))
# 運行結果 {"a1": true, "b1": "hello", "c1": [1, 2, 3], "d1": "name=yoyo&age=18"}

方法二:使用猴子補丁來解決

from json import JSONEncoder

def new_default(self, obj):
    if isinstance(obj, MyDefined):
        return str(obj)
    else:
        return json.JSONEncoder.default(self, obj)

JSONEncoder.default = new_default
print(json.dumps(aa))
# 運行結果 {"a1": true, "b1": "hello", "c1": [1, 2, 3], "d1": "name=yoyo&age=18"}

替換模塊

還有一個比較實用的例子,很多代碼用到 import json,后來發現ujson性能更高,
如果覺得把每個文件的import json 改成 import ujson as json成本較高,或者說想測試一下用ujson替換json是否符合預期,只需要在入口加上:

import json  
import ujson  

def monkey_patch_json():  
    json.__name__ = 'ujson'  
    json.dumps = ujson.dumps  
    json.loads = ujson.loads  

monkey_patch_json()  

運行中替換或者添加方法是非常有用的,比如說在單元測試中,有些負責和外界服務通信的函數就需要替換掉,方便測試。
這個技巧不僅很常用,而且在你最終決定要修改代碼之前還可以保持代碼的可維護性,是一個非常重要的技巧。
還有比如你在pip安裝第三方包的時候,使用過程中發現某個方法有一些bug,或者不兼容中文的情況,這時候不要去改源碼(雖然改源碼能解決,但不利於后期維護,后期你換個電腦,重新pip安裝的時候,就忘記之前改哪里了)
像這種情況下,可以自己寫個方法去替換第三方包原來的方法,非常實用!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM