如何在Python3中pickle和unpickle到可移植字符串

如何在Python3中pickle和unpickle到可移植字符串,python,python-3.x,serialization,unicode,Python,Python 3.x,Serialization,Unicode,我需要将一个Python3对象pickle到一个字符串,我想从Travis CI构建中的环境变量中取消pickle。问题是,我似乎找不到在Python3中pickle到可移植字符串(unicode)的方法: 导入操作系统,pickle 从my_模块导入MyPickleableClass obj={'cls':MyPickleableClass,'other_stuf':'(…)} pickled=pickle.dumps(obj) #引发TypeError:应为str,而不是字节 os.envi

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

导入操作系统,pickle
从my_模块导入MyPickleableClass
obj={'cls':MyPickleableClass,'other_stuf':'(…)}
pickled=pickle.dumps(obj)
#引发TypeError:应为str,而不是字节
os.environ['pickled']=pickled
#引发UnicodeDecodeError:“utf-8”编解码器无法解码字节0xbb(…)
os.environ['pickled']=pickled.decode('utf-8')
pickle.load(os.environ['pickled'])
有没有一种方法可以将复杂对象(如
datetime.datetime
序列化为unicode或Python3中的其他字符串表示形式,我可以将其传输到其他机器并反序列化)

更新 我已经测试了@kindall建议的解决方案,但是
pickle.dumps(obj,0.decode()
引发了
UnicodeDecodeError
。尽管如此,base64方法仍然有效,但它需要一个额外的解码/编码步骤。该解决方案对Python2.x和Python3.x都有效

#encode返回字节,因此需要将其解码为字符串
pickled=pickle.load(codecs.decode(pickled.encode(),'base64')).decode()
类型(酸洗)#
unpickled=pickle.load(codecs.decode(pickled.encode(),'base64'))

如果您想在环境中存储字节,而不是编码文本,这就是它的用途

这在Windows上不起作用。(正如文档所暗示的,您应该检查自己是否在3.2+上,而不是仅仅假设Unix在使用,Windows不在使用…)因此,您需要将字节走私到可以编码的内容中,而不管您的系统编码是什么,例如,使用
反斜杠转义
,甚至
十六进制
。例如:

if os.supports_bytes_environ:
    environb['pickled'] = pickled
else:
    environ['pickled'] = codecs.encode(pickled, 'hex')
pickle.dumps()
生成一个
bytes
对象。期望这些任意字节是有效的UTF-8文本(通过尝试将其从UTF-8解码为字符串而做出的假设)是非常乐观的。如果成功的话,那将是一个巧合

一种解决方案是使用完全使用ASCII字符的旧酸洗协议。这仍然显示为
字节
,但由于它仅为ASCII码,因此可以在没有压力的情况下解码为字符串:

pickled = pickle.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更大),但我还没有对它进行严格的测试。使用您的数据进行测试,然后查看。

我认为最简单的答案,特别是如果您不关心Windows,就是按照中的建议将字节存储在环境中

但是,如果您想要干净且可调试的东西,您可能会更喜欢使用设计为基于文本的格式的东西

pickle
确实有一个“纯文本”协议0,如中所述。它当然比协议3或协议4更具可读性,但它仍然不是我真正想读的东西

更好,但它无法处理开箱即用的
datetime
。对于需要编码的少数类型,您可以提出自己的编码(stdlib的模块是可扩展的),或者使用类似的方法。为您关心的每种类型提供自定义编码通常比一般的“在图灵完整协议中打包任意类型”方案(如
pickle
jsonpickle
)更安全、更高效、更可读,但当然也需要更多的工作,特别是如果您有很多额外的类型

允许您用JSON定义语言,类似于在XML中所做的。它带有一个内置的
日期时间
,Python库知道如何使用它

有一个标准的扩展存储库,其中包含许多JSON没有的类型,包括一个。大多数人已经知道如何将
datetime
对象编码到该类型,以及如何从该类型编码对象。如果您需要YAML之外的其他类型,那么它被设计为声明式可扩展的。有些库相当于
jsonpickle
,如果您真的需要,它们可以动态定义新类型


最后,您可以随时编写XML语言。

类似于JSON的东西?是的,如果可能的话,我更喜欢JSON这样更安全的格式。pickle与可执行代码一样好,在envvar中运行任意代码对我来说似乎相当肮脏,即使由于应用程序的原因,它目前还不是一个安全漏洞。在你真正需要这种灵活性之前,不要求助于泡菜;您当然不需要使用
datetime
。这是
environb
的一个好技巧。我不知道这一点。不幸的是,由于空字节,您仍然无法将纯二进制文件放入
environb
。分配给
environo
项只是为了示例。我真正需要的是通过web表单将序列化字符串放入Travis CI环境变量中。@PeterHudec:好的,这只是为了举例,因为bobince是正确的;如果空间效率真的很重要的话,您肯定希望在可能的情况下使用pickle协议4和
environb
,或者甚至可能使用pickle协议4和bzip。对于Windows,我会测试pickle协议4+bzip+base64和pickle协议0,但我猜前者更小。但是我假设每个CI构建都有几百字节的内存/带宽等。这两种方式都不值得担心。@abarnert第一种解决方案不适用于协议4,只适用于0,即ascii,因此可以进行解码。base64的另一个解决方案可以使用任何协议,默认情况下将使用pickle.DEF