如何在Python 3

时间:2015-05-26 21:51:58

标签: python python-3.x serialization unicode

我需要将一个Python3对象pickle到一个字符串,我想从Travis CI构建中的环境变量中取消。问题是我似乎无法在Python3中找到一种方法来腌制可移植字符串(unicode):

import os, pickle    

from my_module import MyPickleableClass


obj = {'cls': MyPickleableClass, 'other_stuf': '(...)'}

pickled = pickle.dumps(obj)

# raises TypeError: str expected, not bytes
os.environ['pickled'] = pickled

# raises UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbb (...)
os.environ['pickled'] = pickled.decode('utf-8')

pickle.loads(os.environ['pickled'])

有没有办法将像datetime.datetime这样的复杂对象序列化为unicode或Python3中的其他字符串表示,我可以将它转移到另一台机器并反序列化?

更新

我测试了@kindall建议的解决方案,但pickle.dumps(obj, 0).decode()提出了UnicodeDecodeError。然而, base64 方法有效但需要额外解码/编码步骤。该解决方案适用于Python2.x和Python3.x。

# encode returns bytes so it needs to be decoded to string
pickled = pickle.loads(codecs.decode(pickled.encode(), 'base64')).decode()

type(pickled)  # <class 'str'>

unpickled = pickle.loads(codecs.decode(pickled.encode(), 'base64'))

3 个答案:

答案 0 :(得分:22)

pickle.dumps()生成bytes个对象。期望这些任意字节是有效的UTF-8文本(通过尝试将其解码为UTF-8中的字符串而做出的假设)非常乐观。如果有效的话就巧合了!

一种解决方案是使用完全使用ASCII字符的旧酸洗协议。这仍然是bytes,但由于它只有ASCII,因此可以解压缩为没有压力的字符串:

pickled = pickled.dumps(obj, 0).decode()

您还可以使用其他编码方法将二进制pickle对象编码为文本,例如base64:

import codecs
pickled = codecs.encode(pickle.dumps(obj), "base64").decode()

解码将是:

unpickled = pickle.loads(codecs.decode(pickled.encode(), "base64"))

对协议0使用pickle似乎导致比base64编码的二进制pickle更短的字符串(abarnert建议的十六进制编码将比base64更大),但我没有严格测试它什么的用你的数据测试它,然后看看。

答案 1 :(得分:1)

如果你想在环境中存储字节而不是编码文本,那就是environb的用途。

这在Windows上不起作用。 (正如文档所暗示的那样,如果您使用的是3.2+,则应该检查os.supports_bytes_environ,而不是仅仅假设Unix没有,而Windows则不会......)为此,您需要将字节走私到无论您的系统编码是什么,都可以编码,例如,使用backslash-escape,甚至是hex。所以,例如:

if os.supports_bytes_environ:
    environb['pickled'] = pickled
else:
    environ['pickled'] = codecs.encode(pickled, 'hex')

答案 2 :(得分:0)

我认为最简单的答案,特别是如果您不关心Windows,只是将字节存储在环境中,如my other answer中所述。

但是如果你想要一些干净和可调试的东西,你可能会更乐意使用设计为基于文本的格式。

pickle确实有&#34;纯文本&#34;协议0,如kindall's answer中所述。它确实比协议3或4更具可读性,但它仍然不是我实际想要阅读的东西。

JSON更好,但它无法开箱即用datetime。您可以为需要编码的少数类型提供自己的编码(stdlib&#39; s json模块是可扩展的),或使用jsonpickle之类的内容。对于您关心的每种类型,它通常更安全,更有效,更易读,而不是一般的&#34;包装图灵完整协议中的任意类型&#34;像picklejsonpickle这样的方案,但当然它还有更多的工作,特别是如果你有很多额外的类型。

JSON Schema允许您使用JSON定义语言,类似于您在XML中执行的操作。它带有内置的date-time String format,而Python的jsonschema库知道如何使用它。

YAML有一个标准扩展存储库,其中包含JSON不包含的许多类型,包括timestamp。大多数the zillion 'yaml' modules for Python已经知道如何对此类型的datetime对象进行编码。如果您需要YAML所包含的其他类型,那么它的设计是可以声明性地扩展。如果您确实需要,那么库有相当于jsonpickle的库,可以动态定义新类型。

最后,您总是可以编写XML语言。