当前位置: 首页 > 科技观察

一种更好的序列化Python对象的方法

时间:2023-03-12 09:58:40 科技观察

许多Python标准库都有一些未被充分认识的宝石。其中之一是允许基于参数类型的简单而优雅的函数分配。此功能非常适合序列化任意对象——例如JSON或WebAPI的结构化日志。任何人都应该看到这一点:不过这没什么大不了的。json模块(继承自simplejson的API)提供了两种序列化对象的方法:1.实现一个default()函数,接收一个对象作为参数,返回一些JSONEncoder可以理解的东西;2.你自己实现它或者将它子类化转换成一个JSONEncoder并作为cls传给dump方法。您可以自己实现它或简单地覆盖JSONEncoder.default()方法。由于一些第三方实现希望兼容大部分程序,所以都不同程度地模仿了json模块的API1。可扩展性上述所有方法的共同点是它们不可扩展:它们不提供对新类型的支持。您的default()函数需要了解您要序列化的所有自定义类型。这意味着您要么像这样编写您的函数:这??看起来真的很痛苦,因为您需要在一个地方为所有不同类型的对象递增序列化结果2。或者,您可以尝试自己想出一个通用的解决方案,就像Pyramid的JSON渲染器在JSON.add_adapter中所做的那样,它使用被广泛低估的zope.interface的适配器注册表3。另一方面,Django本身实现了一个DjangoJSONEncoder,它是json.JSONEncoder的子类,它知道如何对日期、时间、UUID和承诺进行编码。但除此之外,你又要靠自己了。如果您想深入研究Django和WebAPI,那么您可能已经准备好使用Django的REST框架。他们实现了一个完整的序列化系统,不仅仅是json.dump()数据。最后,为了完整起见,我觉得有必要在structlog中提及我自己从第一天起就非常讨厌的解决方案:向您的类添加一个__structlog__方法,返回像__str__一样的序列化表示方法。请不要重蹈我的覆辙。标签:软件小丑。JSON已经变得非常流行,但我们的序列化解决方案仍然很不完整。我个人想要的是能够注册一个集中的序列化工具,但以分散的方式使用它,这样我就不需要对我的类(或更糟的是第三方类)进行任何更改。在Python3.4的PEP443中以PEP443的形式给出了这个问题的一个很好的解决方案:functools.singledispatch(老式的Python版本也可以在PyPI上获得)。简而言之,您可以定义一个默认函数并根据第一个参数的类型注册该函数的额外版本:现在您还可以在datetime实例上调用to_serializable(),singledispatch将选择正确的函数。这种方法允许您将序列化程序放置在任何您想要的位置:在类中、在单独的模块中或在JSON相关代码中。你选!但是您的类保持干净,并且您不需要庞大繁琐的if-elif-else分支。更深入一点显然,@singledispatch的用法比JSON更深入。一般来说,为不同类型的对象绑定不同的行为和独立的序列化方法是普遍适用的。我的一些校对员提到他们尝试用字典类和其他类似的“残暴”事物来近似可调用对象。换句话说,@singledispatch是一个很早就存在的函数,但是你忽略了。附言当然,PyPI中也有一个*multiple*dispatch。Footnote1.然而,对于非常有名的一个:UltraJSON根本不支持自定义对象的序列化,而且python-rapidjson只支持default()函数。2.使用attrs可以很好的管理!也许你应该使用attrs!3。不幸的是,自zope.component端口以来,Pyramid使用的API没有被记录下来。4.听说标准库加入singlepatch的最初动力来自pprint的更优雅的实现(虽然从未实现)