如何在 Python 3 中对便携式字符串进行 pickle 和 unpickle
How to pickle and unpickle to portable string in Python 3
我需要将一个 Python3 对象 pickle 为一个字符串,我想在 Travis CI 构建中从环境变量中取消 pickle。问题是我似乎找不到在 Python3:
中 pickle 到可移植字符串 (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 方法仍然有效,但它需要 额外的 decode/encode 步骤。该解决方案适用于 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'))
如果您想在环境中存储字节而不是编码文本,这就是 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')
pickle.dumps()
生成一个 bytes
对象。期望这些任意字节是有效的 UTF-8 文本(您通过尝试将其解码为 UTF-8 字符串所做的假设)是非常乐观的。能成功就纯属巧合了!
一种解决方案是使用完全使用 ASCII 字符的较旧的酸洗协议。这仍然是 bytes
,但由于它是 ASCII-only,因此可以毫无压力地解码为字符串:
pickled = pickle.dumps(obj, 0).decode()
您还可以使用其他一些编码方法将二进制腌制对象编码为文本,例如 base64:
import codecs
pickled = codecs.encode(pickle.dumps(obj), "base64").decode()
解码将是:
unpickled = pickle.loads(codecs.decode(pickled.encode(), "base64"))
将 pickle
与协议 0 一起使用似乎会产生比 base64 编码的二进制 pickle 更短的字符串(并且 abarnert 的十六进制编码建议将比 base64 更大),但我还没有测试过它严格或任何东西。使用您的数据对其进行测试并查看。
我认为 最简单的 答案,特别是如果您不关心 Windows,就是将字节存储在环境中,如 .
但是如果您想要干净且可调试的东西,您可能会更乐意使用设计为基于文本的格式的东西。
pickle
确实有一个 "plain text" 协议 0,如 中所述。它肯定比协议 3 或 4 更具可读性,但它仍然不是我实际上想要阅读的东西。
JSON is much nicer, but it can't handle datetime
out of the box. You can come up with your own encoding (the stdlib's json
module is extensible) for the handful of types you need to encode, or use something like jsonpickle
。与 pickle
或 jsonpickle
等一般 "pack arbitrary types in a turing-complete protocol" 方案相比,为您关心的每种类型提供自定义编码通常更安全、更高效且更具可读性,但当然它也更多工作,特别是如果你有很多额外的类型。
JSON Schema lets you define languages in JSON, similar to what you'd do in XML. It comes with a built-in date-time
String format, and the jsonschema
Python 的库知道如何使用它。
YAML has a standard extension repository that includes many types JSON doesn't, including a timestamp. Most of the zillion 'yaml' modules for Python 已经知道如何将 datetime
对象编码为这种类型。如果您需要 YAML 包含的类型之外的其他类型,它被设计为可声明式扩展。如果您确实需要,有些库的功能相当于 jsonpickle
,即时定义新类型。
最后,您始终可以编写 XML 语言。
我需要将一个 Python3 对象 pickle 为一个字符串,我想在 Travis CI 构建中从环境变量中取消 pickle。问题是我似乎找不到在 Python3:
中 pickle 到可移植字符串 (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 方法仍然有效,但它需要 额外的 decode/encode 步骤。该解决方案适用于 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'))
如果您想在环境中存储字节而不是编码文本,这就是 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')
pickle.dumps()
生成一个 bytes
对象。期望这些任意字节是有效的 UTF-8 文本(您通过尝试将其解码为 UTF-8 字符串所做的假设)是非常乐观的。能成功就纯属巧合了!
一种解决方案是使用完全使用 ASCII 字符的较旧的酸洗协议。这仍然是 bytes
,但由于它是 ASCII-only,因此可以毫无压力地解码为字符串:
pickled = pickle.dumps(obj, 0).decode()
您还可以使用其他一些编码方法将二进制腌制对象编码为文本,例如 base64:
import codecs
pickled = codecs.encode(pickle.dumps(obj), "base64").decode()
解码将是:
unpickled = pickle.loads(codecs.decode(pickled.encode(), "base64"))
将 pickle
与协议 0 一起使用似乎会产生比 base64 编码的二进制 pickle 更短的字符串(并且 abarnert 的十六进制编码建议将比 base64 更大),但我还没有测试过它严格或任何东西。使用您的数据对其进行测试并查看。
我认为 最简单的 答案,特别是如果您不关心 Windows,就是将字节存储在环境中,如
但是如果您想要干净且可调试的东西,您可能会更乐意使用设计为基于文本的格式的东西。
pickle
确实有一个 "plain text" 协议 0,如
JSON is much nicer, but it can't handle datetime
out of the box. You can come up with your own encoding (the stdlib's json
module is extensible) for the handful of types you need to encode, or use something like jsonpickle
。与 pickle
或 jsonpickle
等一般 "pack arbitrary types in a turing-complete protocol" 方案相比,为您关心的每种类型提供自定义编码通常更安全、更高效且更具可读性,但当然它也更多工作,特别是如果你有很多额外的类型。
JSON Schema lets you define languages in JSON, similar to what you'd do in XML. It comes with a built-in date-time
String format, and the jsonschema
Python 的库知道如何使用它。
YAML has a standard extension repository that includes many types JSON doesn't, including a timestamp. Most of the zillion 'yaml' modules for Python 已经知道如何将 datetime
对象编码为这种类型。如果您需要 YAML 包含的类型之外的其他类型,它被设计为可声明式扩展。如果您确实需要,有些库的功能相当于 jsonpickle
,即时定义新类型。
最后,您始终可以编写 XML 语言。