泡菜的九大缺陷

2020-07-16 17:08:33

Python的icklemodule是序列化和反序列化对象的一种非常方便的方法。它不需要模式,可以处理任意的Python对象。但它也有问题。这篇文章简要解释了这些问题。

有些人会告诉你永远不要用泡菜,因为它不好。我不会走那么远的。我要说的是,只有当你对泡菜的九个缺点没有意见的时候,才可以使用泡菜:

泡菜可以是手工制作的,当你解开泡菜时会有恶意的影响。因此,您永远不应该取消挑选您不信任的数据。

不安全不是因为Pickle包含代码,而是因为它们通过调用在Pickle中命名的构造函数来创建对象。任何可调用的对象都可以用来代替您的类名来构造对象。恶意的Pickles将使用其他Python可调用函数作为“构造函数”。例如,危险的Pickle可能执行“os.system(‘rm-rf/’)”,而不是执行“Models.MyObject(17)”。UnPickler无法区分“Models.MyObject”和“os.system”。这两个都是它可以解析的名称,产生它可以调用的东西。UnPickler按照Pickle的指示执行它们中的任何一个。

更多细节,包括一个例子,请参见Supaken的帖子Dangerin Python的标准库。

因为泡菜存储对象的结构,所以当它们未泡菜时,它们的结构与您泡菜时的结构相同。这听起来像是一件好事,也正是泡菜的设计初衷。但是,如果您的代码在制作泡菜的时间和使用它的时间之间发生了变化,则您的对象可能与您的代码不一致。对象仍将具有由旧代码创建的结构,但它们将使用新代码运行。

例如,如果您在制作泡菜后添加了一个属性,则泡菜中的对象将不具有该属性。您的代码将预期该属性,从而导致问题。

Pickles的最大便利在于它们将序列化您的对象具有的任何结构。创建序列化结构不需要额外的工作。但这也带来了自身的问题。您真的希望将日期时间序列化为日期时间吗?或者作为iso8601字符串?你没有条件:他们会是约会时间。

您不仅不必指定序列化形式,还不能指定它。

Pickle是隐式的:它们序列化对象中的所有内容,甚至是您不想序列化的数据。例如,您可能有一个属性是您不想序列化的计算缓存。Pickle没有一种方便的方式跳过该属性。

更糟糕的是,如果您的对象包含不能进行PICLE的属性,比如打开的文件对象,PICLE不会跳过它,它会坚持尝试PICLE,然后抛出异常。

泡菜存储对象的整个结构。当PICLE模块创建您的对象时,它不会调用您的__init__方法,因为对象已经创建。

这可能会令人惊讶,因为没有任何其他地方的对象是不调用__init__而产生的。这里的逻辑是,在制作泡菜的进程中首次创建对象时,已经调用了__init__。

但是您的__init__方法可能会执行一些重要的工作,比如打开fileobject。您的未酸洗对象将处于与您的__init__方法不一致的状态。或者,您的__init__可能会记录有关正在创建的对象的信息。未酸洗的对象不会出现在日志中。

Pickles是特定于Python的,并且只能被其他Python程序使用。这并不是严格意义上的,你可以找到其他语言的包可以使用Pickle,但是它们很少。它们自然会局限于跨语言通用列表/字典对象结构,在这一点上,您也可以只使用JSON。

Pickle是一个二进制数据流(实际上是抽象执行引擎的指令)。如果将泡菜作为纯文件打开,则无法读取其内容。知道泡菜中有什么的唯一方法是使用泡菜模块来加载它。这可能会使调试变得困难,因为您可能无法在PICLE文件中搜索您感兴趣的数据:

函数和类是Python中的一级对象:您可以将它们存储在列表、字典、属性等中。Pickle将很乐意序列化包含函数和类等可调用对象。但它不会将代码存储在Pickle中,而只存储函数或类的名称。

Pickle不是一种移动或存储代码的方式,尽管它们看起来像是一种方式。当您取消挑选数据时,函数的名称用于查找正在运行的进程中的现有代码。

与其他序列化技术相比,PICLE可能会很慢,正如BenFrederickson在Don‘t PickleYour Data中所展示的那样。

其中一些问题可以通过向您的类添加特殊方法来解决,如__getstate__或__Reduce__。但是,一旦您沿着这条路开始,您也可以使用另一种序列化方法,该方法从一开始就没有这些缺陷。

有很多其他方法可以序列化对象,从普通的JSON到更时髦的替代方法,如棉花糖、猫、协议缓冲区等等。

我对其中任何一个都没有强烈的推荐。正确的答案取决于你问题的细节。甚至可能是泡菜。

名称是必填项。电子邮件或Web为必填项。电子邮件不会显示,我不会给您发送垃圾邮件。您的网站不会被搜索引擎编入索引。