日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

黑马程序员python笔记_#华为云·寻找黑马程序员# 如何实现一个优雅的Python的Json序列化库...

發(fā)布時(shí)間:2023/12/19 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 黑马程序员python笔记_#华为云·寻找黑马程序员# 如何实现一个优雅的Python的Json序列化库... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【小宅按】在Python的世界里,將一個(gè)對象以json格式進(jìn)行序列化或反序列化一直是一個(gè)問題。Python標(biāo)準(zhǔn)庫里面提供了json序列化的工具,我們可以簡單的用json.dumps來將一個(gè)對象序列化。但是這種序列化僅支持python內(nèi)置的基本類型,對于自定義的類,我們將得到Object of type A is not JSON serializable的錯(cuò)誤。

有很多種方法可以用來支持這種序列化,這里有一個(gè)很長的關(guān)于這個(gè)問題的討論。總結(jié)起來,基本上有兩種還不錯(cuò)的思路:

  • 利用標(biāo)準(zhǔn)庫的接口:從python標(biāo)準(zhǔn)json庫中的JSONDecoder繼承,然后自定義實(shí)現(xiàn)一個(gè)default方法用來自定義序列化過程
  • 利用第三方庫實(shí)現(xiàn):如jsonpickle jsonweb json-tricks等
  • 利用標(biāo)準(zhǔn)庫的接口的問題在于,我們需要對每一個(gè)自定義類都實(shí)現(xiàn)一個(gè)JSONDecoder.default接口,難以實(shí)現(xiàn)代碼復(fù)用。

    利用第三方庫,對我們的代碼倒是沒有任何侵入性,特別是jsonpickle,由于它是基于pickle標(biāo)準(zhǔn)序列化庫實(shí)現(xiàn),可以實(shí)現(xiàn)像pickle一樣序列化任何對象,一行代碼都不需要修改。

    但是我們觀察這類第三方庫的輸出的時(shí)候,會(huì)發(fā)現(xiàn)所有的這些類庫都會(huì)在輸出的json中增加一個(gè)特殊的標(biāo)明對象類型的屬性。這是為什么呢?Python是一門動(dòng)態(tài)類型的語言,我們無法在對象還沒有開始構(gòu)建的時(shí)候知道對象的某一屬性的類型信息,為了對反序列化提供支持,看起來確實(shí)是不得不這么做。

    有人可能覺得這也無可厚非,似乎不影響使用。但是在跨語言通信的時(shí)候,這就成為了一個(gè)比較麻煩的問題。比如我們有一個(gè)Python實(shí)現(xiàn)的API,客戶端發(fā)送了一個(gè)json請求過來,我們想在統(tǒng)一的一個(gè)地方將json反序列化為我們Python代碼的對象。由于客戶端不知道服務(wù)器端的類型信息,json請求里面就沒法加入這樣的類型信息,這也就導(dǎo)致這樣的類庫在反序列化的時(shí)候遇到問題。

    能不能有一個(gè)相對完美的實(shí)現(xiàn)呢?先看一下我們理想的json序列化庫的需求:

  • 我們希望能簡單的序列化任意自定義對象,只添加一行代碼,或者不加入任何代碼
  • 我們希望序列化的結(jié)果不加入任何非預(yù)期的屬性
  • 我們希望能按照指定的類型進(jìn)行反序列化,能自動(dòng)處理嵌套的自定義類,只需要自定義類提供非常簡單的支持,或者不需要提供任何支持
  • 我們希望反序列化的時(shí)候能很好的處理屬性不存在的情況,以便在我們加入某一屬性的時(shí)候,可以設(shè)置默認(rèn)值,使得舊版本的序列化結(jié)果可以正確的反序列化出來
  • 如果有一個(gè)json庫能支持上面的四點(diǎn),那就基本是比較好用的庫了。下面我們來嘗試實(shí)現(xiàn)一下這個(gè)類庫。

    對于我們想要實(shí)現(xiàn)的幾個(gè)需求,我們可以建立下面這樣的測試來表達(dá)我們所期望的庫的API設(shè)計(jì):

    class SerializableModelTest(unittest.TestCase):def test_model_serializable(self):class A(SerializableModel):def __init__(self, a, b):super().__init__()self.a = aself.b = b if b is not None else B(0)@propertydef id(self):return self.adef _deserialize_prop(self, name, deserialized):if name == 'b':self.b = B.deserialize(deserialized)returnsuper()._deserialize_prop(name, deserialized)class B(SerializableModel):def __init__(self, b):super().__init__()self.b = bself.assertEqual(json.dumps({'a': 1, 'b': {'b': 2}, 'long_attr': None}), A(1, B(2)).serialize())self.assertEqual(json.dumps({'a': 1, 'b': None}), A(1, None).serialize())self.assertEqual(A(1, B(2)), A.deserialize(json.dumps({'a': 1, 'b': {'b': 2}})))self.assertEqual(A(1, None), A.deserialize(json.dumps({'a': 1, 'b': None})))self.assertEqual(A(1, B(0)), A.deserialize(json.dumps({'a': 1})))

    這里我們希望通過繼承的方式來添加支持,這將在反序列化的時(shí)候提供一個(gè)好處。因?yàn)橛辛怂覀兙涂梢灾苯邮褂肁.deserialize方法來反序列化,而不需要提供任何其他的反序列化函數(shù)參數(shù),比如這樣json.deserialize(serialized_str, A)。

    同時(shí)為了驗(yàn)證我們的框架不會(huì)將@property屬性序列化或者反序列化,我們特意在類A中添加了這樣一個(gè)屬性。

    由于在反序列化的時(shí)候,框架是無法知道某一個(gè)對象屬性的類型信息,比如測試中的A.b,為了能正確的反序列化,我們需要提供一點(diǎn)簡單的支持,這里我們在類A中覆蓋實(shí)現(xiàn)了一個(gè)父類的方法_deserialize_prop對屬性b的反序列化提供支持。

    當(dāng)我們要反序列化一個(gè)之前版本的序列化結(jié)果時(shí),我們希望能正確的反序列化并使用我們提供的默認(rèn)值作為最終的反序列化值。

    如果能有一個(gè)類可以讓上面的測試通過,相信那個(gè)類就是我們所需要的類了。這樣的類可以實(shí)現(xiàn)為如下:

    class ModelBase:@staticmethoddef is_normal_prop(obj, key):is_prop = isinstance(getattr(type(obj), key, None), property)is_constant = re.match('^[A-Z_0-9]+$', key)return not (key.startswith('__') or callable(getattr(obj, key)) or is_prop or is_constant)@staticmethoddef is_basic_type(value):return value is None or type(value) in [int, float, str, list, tuple, bool, dict]def _serialize_prop(self, name):value = getattr(self, name)if isinstance(value, (tuple, list)):try:json.dumps(value)return valueexcept Exception:return [v._as_dict() for v in value]return valuedef _as_dict(self):keys = dir(self)props = {}for key in keys:if not ModelBase.is_normal_prop(self, key):continuevalue = self._serialize_prop(key)if not (ModelBase.is_basic_type(value) or isinstance(value, ModelBase)):raise Exception('unkown value to serialize to dict: key={}, value={}'.format(key, value))props[key] = value if self.is_basic_type(value) else value._as_dict()return propsdef _short_prop(self, name):value = getattr(self, name)if isinstance(value, (tuple, list)):try:json.dumps(value)return valueexcept Exception:return [v._as_short_dict() for v in value]return valuedef _as_short_dict(self):keys = dir(self)props = {}for key in keys:if not ModelBase.is_normal_prop(self, key):continuevalue = self._short_prop(key)if not (ModelBase.is_basic_type(value) or isinstance(value, ModelBase)):raise Exception('unkown value to serialize to short dict: key={}, value={}'.format(key, value))props[key] = value if self.is_basic_type(value) else value._as_short_dict()return propsdef serialize(self):return json.dumps(self._as_dict(), ensure_ascii=False)def _deserialize_prop(self, name, deserialized):setattr(self, name, deserialized)@classmethoddef deserialize(cls, json_encoded):if json_encoded is None:return Noneimport inspectargs = inspect.getfullargspec(cls)args_without_self = args.args[1:]obj = cls(*([None] * len(args_without_self)))data = json.loads(json_encoded, encoding='utf8') if type(json_encoded) is str else json_encodedkeys = dir(obj)for key in keys:if not ModelBase.is_normal_prop(obj, key):continueif key in data:obj._deserialize_prop(key, data[key])return objdef __str__(self):return self.serialize()def _prop_eq(self, name, value, value_other):return value == value_otherdef __eq__(self, other):if other is None or other.__class__ is not self.__class__:return Falsekeys = dir(self)for key in keys:if not ModelBase.is_normal_prop(self, key):continuevalue, value_other = getattr(self, key), getattr(other, key)if not (ModelBase.is_basic_type(value) or isinstance(value, ModelBase)):raise Exception('unsupported value to compare: key={}, value={}'.format(key, value))if value is None and value_other is None:continueif (value is None and value_other is not None) or (value is not None and value_other is None):return Falseif not self._prop_eq(key, value, value_other):return Falsereturn Truedef short_repr(self):return json.dumps(self._as_short_dict(), ensure_ascii=False)

    為了更進(jìn)一步提供支持,我們將最終的類命名為ModelBase,因?yàn)橥ǔN覀円蛄谢蚍葱蛄谢膶ο蠖际俏覀冃枰厥鈱Υ膶ο?#xff0c;且我們通常稱其為模型,我們一般也會(huì)將其放在一個(gè)單獨(dú)models模塊中。
    作為一個(gè)模型的基類,我們還添加了一些常用的特性,比如:

  • 支持標(biāo)準(zhǔn)的格式化接口__str__,這樣我們在使用'{}'.format(a)的時(shí)候,就可以得到一個(gè)更易于理解的輸出
  • 提供了一個(gè)縮短的序列化方式,在我們有時(shí)候不想直接輸出某一個(gè)特別長的屬性的時(shí)候很有用
  • 提供了基于屬性值的比較方法
  • 自定義類的屬性可以為基礎(chǔ)的Python類型,或者由基礎(chǔ)Python類型構(gòu)成的list tuple dict
  • 在使用這個(gè)類的時(shí)候,當(dāng)然也是有一些限制的,主要的限制如下:

  • 當(dāng)某一屬性為自定義類的類型的時(shí)候,需要子類覆蓋實(shí)現(xiàn)_deserialize_prop方法為反序列化過程提供支持
  • 當(dāng)某一屬性為由自定義類構(gòu)成的一個(gè)list tuple dict復(fù)雜對象時(shí),需要子類覆蓋實(shí)現(xiàn)_deserialize_prop方法為反序列化過程提供支持
  • 簡單屬性必須為python內(nèi)置的基礎(chǔ)類型,比如如果某一屬性的類型為numpy.float64,序列化反序列化將不能正常工作
  • 雖然有上述限制,但是這正好要求我們在做模型設(shè)計(jì)的時(shí)候保持克制,不要將某一個(gè)對象設(shè)計(jì)得過于復(fù)雜。比如如果有屬性為dict類型,我們可以將這個(gè)dict抽象為另一個(gè)自定義類型,然后用類型嵌套的方式來實(shí)現(xiàn)。

    到這里這個(gè)基類就差不多可以支撐我們?nèi)粘5拈_發(fā)需要了。當(dāng)然對于這個(gè)簡單的實(shí)現(xiàn)還有可能有其他的需求或者問題,大家如有發(fā)現(xiàn),歡迎留言交流。

    更多精彩內(nèi)容,請滑至頂部點(diǎn)擊右上角關(guān)注小宅哦~


    來源:華為云社區(qū)原創(chuàng) 作者:Bright Liao

    總結(jié)

    以上是生活随笔為你收集整理的黑马程序员python笔记_#华为云·寻找黑马程序员# 如何实现一个优雅的Python的Json序列化库...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。