python 猴子補(bǔ)丁(monkey patch)
寫(xiě)了一段時(shí)間java切回寫(xiě)python偶爾會(huì)出現(xiàn)一些小麻煩,比如:在java中自定義對(duì)象變成json串很簡(jiǎn)單,調(diào)用一個(gè)方法就行,但同樣的轉(zhuǎn)換在python中卻不太容易實(shí)現(xiàn)。在尋找python自定義對(duì)象轉(zhuǎn)json串的過(guò)程中,接觸到了猴子補(bǔ)丁這個(gè)東西,感覺(jué)還有點(diǎn)意思;本文先實(shí)現(xiàn)python自定義對(duì)象轉(zhuǎn)json串,再簡(jiǎn)單談一下猴子補(bǔ)丁。
python自定義對(duì)象轉(zhuǎn)json串
python自帶的json包不支持自定義對(duì)象轉(zhuǎn)json串,在python中用json.dumps轉(zhuǎn)自定義對(duì)象時(shí)會(huì)報(bào)異常class is not JSON serializable,通過(guò)增加一段代碼補(bǔ)丁(稱(chēng)作猴子補(bǔ)?。┍憧蓪?shí)現(xiàn)自定義轉(zhuǎn)換,補(bǔ)丁代碼如下:
from json import JSONEncoder def _default(self, obj): return getattr(obj.__class__, "to_json", _default.default)(obj) _default.default = JSONEncoder().default default.JSONEncoder.default = _default
同時(shí)在自定義對(duì)象里面實(shí)現(xiàn)to_json方法。
class Tmp: def __init__(self, id, name): self.id = id self.name = name def to_json(): # 返回自定義對(duì)象json串 pass
最后保證補(bǔ)丁代碼在自定義對(duì)象轉(zhuǎn)json之前執(zhí)行過(guò)一次即可。
通過(guò)補(bǔ)丁代碼我們可以看到,代碼替換了json包的默認(rèn)轉(zhuǎn)json的方法,運(yùn)行了補(bǔ)丁代碼后,轉(zhuǎn)json的過(guò)程變成了先找對(duì)象的to_json屬性,在沒(méi)有to_json屬性的情況下才使用默認(rèn)的JSONEncoder.default的方法,也就是通過(guò)這么一個(gè)patch,增加了json包原來(lái)沒(méi)有的功能。
猴子補(bǔ)丁
關(guān)于猴子補(bǔ)丁為啥叫猴子補(bǔ)丁,據(jù)說(shuō)是這樣子的:
這個(gè)叫法起源于Zope框架,大家在修正Zope的Bug的時(shí)候經(jīng)常在程序后面追加更新部分,這些被稱(chēng)作是“雜牌軍補(bǔ)丁(guerilla patch)”,后來(lái)guerilla就漸漸的寫(xiě)成了gorllia((猩猩),再后來(lái)就寫(xiě)了monkey(猴子),所以猴子補(bǔ)丁的叫法是這么莫名其妙的得來(lái)的。
猴子補(bǔ)丁主要有以下幾個(gè)用處:
- 在運(yùn)行時(shí)替換方法、屬性等
- 在不修改第三方代碼的情況下增加原來(lái)不支持的功能
- 在運(yùn)行時(shí)為內(nèi)存中的對(duì)象增加patch而不是在磁盤(pán)的源代碼中增加
例如:上面自定義對(duì)象轉(zhuǎn)json,在原有json包不滿足的條件下,只需要將以上的一個(gè)patch寫(xiě)在一個(gè)文件里自己再import一次,便可實(shí)現(xiàn)自己想要的功能,這是非常方便的。
可以知道猴子補(bǔ)丁的主要功能便是在不去改變?cè)创a的情況下而對(duì)功能進(jìn)行追加和變更;對(duì)于編程過(guò)程中使用一些第三方不滿足需求的情況下,使用猴子補(bǔ)丁是非常方便的。
猴子補(bǔ)丁,算是編程中的一個(gè)技巧了。
拓展
json包默認(rèn)轉(zhuǎn)json的過(guò)程
可以看一下json包里面轉(zhuǎn)json串的過(guò)程:
def _iterencode(o, _current_indent_level): if isinstance(o, basestring): yield _encoder(o) elif o is None: yield 'null' elif o is True: yield 'true' elif o is False: yield 'false' elif isinstance(o, (int, long)): yield str(o) elif isinstance(o, float): yield _floatstr(o) elif isinstance(o, (list, tuple)): for chunk in _iterencode_list(o, _current_indent_level): yield chunk elif isinstance(o, dict): for chunk in _iterencode_dict(o, _current_indent_level): yield chunk else: if markers is not None: markerid = id(o) if markerid in markers: raise ValueError("Circular reference detected") markers[markerid] = o o = _default(o) for chunk in _iterencode(o, _current_indent_level): yield chunk if markers is not None: del markers[markerid]
其實(shí)就是一連串的if-elif-else,將所有的自建對(duì)象都匹配一遍,最后匹配不到的就報(bào)錯(cuò)了,所以自定義對(duì)象轉(zhuǎn)json自然會(huì)有問(wèn)題。
其他實(shí)現(xiàn)自定義對(duì)象轉(zhuǎn)json的方法
其實(shí)json包的源碼文檔里面也有很詳細(xì)的別的自定義對(duì)象轉(zhuǎn)json的方法。
r''' Specializing JSON object decoding:: >>> import json >>> def as_complex(dct): ... if '__complex__' in dct: ... return complex(dct['real'], dct['imag']) ... return dct ... >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}', ... object_hook=as_complex) (1+2j) >>> from decimal import Decimal >>> json.loads('1.1', parse_float=Decimal) == Decimal('1.1') True Specializing JSON object encoding:: >>> import json >>> def encode_complex(obj): ... if isinstance(obj, complex): ... return [obj.real, obj.imag] ... raise TypeError(repr(o) + " is not JSON serializable") ... >>> json.dumps(2 + 1j, default=encode_complex) '[2.0, 1.0]' >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j) '[2.0, 1.0]' >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j)) '[2.0, 1.0]' '''
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python實(shí)現(xiàn)簡(jiǎn)易內(nèi)存監(jiān)控
這篇文章主要介紹了python實(shí)現(xiàn)簡(jiǎn)易內(nèi)存監(jiān)控,每隔3秒獲取系統(tǒng)內(nèi)存,當(dāng)內(nèi)存超過(guò)設(shè)定的警報(bào)值時(shí),獲取所有進(jìn)程占用內(nèi)存并發(fā)出警報(bào)聲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Python導(dǎo)入模塊包原理及相關(guān)注意事項(xiàng)
這篇文章主要介紹了Python導(dǎo)入模塊包原理及相關(guān)注意事項(xiàng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03

Keras 利用sklearn的ROC-AUC建立評(píng)價(jià)函數(shù)詳解

Django 模板中常用的過(guò)濾器實(shí)現(xiàn)

使用django-crontab實(shí)現(xiàn)定時(shí)任務(wù)的示例

python 利用pandas將arff文件轉(zhuǎn)csv文件的方法

淺談python爬蟲(chóng)使用Selenium模擬瀏覽器行為

Python3爬蟲(chóng)之urllib攜帶cookie爬取網(wǎng)頁(yè)的方法