如何将 Python 字典序列化为字符串,然后再返回字典?
Posted
技术标签:
【中文标题】如何将 Python 字典序列化为字符串,然后再返回字典?【英文标题】:How do I serialize a Python dictionary into a string, and then back to a dictionary? 【发布时间】:2011-05-19 12:51:45 【问题描述】:字典里面会有列表和其他字典。
【问题讨论】:
你熟悉pickle
吗?
作为 Python 标准库一部分的模块
【参考方案1】:
JSON 或 YaML 的新替代方案是 NestedText。它支持嵌套在列表和字典中任意深度的字符串。它通过使用缩进来传达嵌套,因此不需要引用或转义。因此,结果往往非常易读。结果看起来像 YaML,但没有所有特殊情况。它特别适用于序列化代码 sn-ps。例如,下面是一个从使用 NestedText 序列化的更大集合中提取的单个测试用例:
base tests:
-
args: --quiet --config test7 files -N configs/subdir
expected:
> Archive: test7-\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d
> «TESTS»/configs/subdir/
> «TESTS»/configs/subdir/file
请注意,整数、浮点数和布尔值会转换为字符串。
【讨论】:
【参考方案2】:pyyaml 也应该在这里提到。它既是人类可读的,又可以序列化任何 python 对象。 pyyaml 托管在这里:https://pypi.org/project/PyYAML
【讨论】:
见pypi.org/project/PyYAML。不过考虑漏洞(例如:ibm.com/support/pages/…) 感谢新链接,我相应地更新了我的答案。【参考方案3】:json
不能做的一件事是dict
用数字索引。下面的sn-p
import json
dictionary = dict(0:0, 1:5, 2:10)
serialized = json.dumps(dictionary)
unpacked = json.loads(serialized)
print(unpacked[0])
会扔
KeyError: 0
因为键被转换为字符串。 cPickle
保留数字类型,解压后的dict
可以立即使用。
【讨论】:
【参考方案4】:这取决于你想用它做什么。如果你只是想保存它,你应该使用pickle
(或者,如果你使用的是CPython 2.x,cPickle
,这样更快)。
>>> import pickle
>>> pickle.dumps('foo': 'bar')
b'\x80\x03q\x00X\x03\x00\x00\x00fooq\x01X\x03\x00\x00\x00barq\x02s.'
>>> pickle.loads(_)
'foo': 'bar'
如果你想让它可读,你可以使用json
:
>>> import json
>>> json.dumps('foo': 'bar')
'"foo": "bar"'
>>> json.loads(_)
'foo': 'bar'
json
但是,它所支持的功能非常有限,而pickle
可以用于任意对象(如果它不能自动工作,该类可以定义__getstate__
来精确指定它应该如何使用腌制)。
>>> pickle.dumps(object())
b'\x80\x03cbuiltins\nobject\nq\x00)\x81q\x01.'
>>> json.dumps(object())
Traceback (most recent call last):
...
TypeError: <object object at 0x7fa0348230c0> is not JSON serializable
【讨论】:
我希望我知道 -1 到底是为了什么。 我猜这个 -1 可能是因为没有提到酸洗固有的安全问题。见***.com/questions/10282175/attacking-pythons-pickle 值得一提的是答案的cPickle部分与python 3.x无关。官方解释见here。简而言之,包的加速 C 版本应该是任何 python 模块的默认选择,如果不可用,模块本身会退回到 python 实现。这封装了来自用户的实现。引用:In Python 3.0... Users should always import the standard version, which attempts to import the accelerated version and falls back to the pure Python version.
"警告pickle 模块不安全。只解开您信任的数据。" - docs【参考方案5】:
Pickle 很棒,但如果您只是序列化基本的 python 类型,我认为值得一提的是 ast
模块中的 literal_eval
以获得更轻量级的解决方案。它基本上是臭名昭著的 eval
函数的“安全”版本,它只允许评估基本的 Python 类型,而不是任何有效的 Python 代码。
例子:
>>> d =
>>> d[0] = range(10)
>>> d['1'] =
>>> d['1'][0] = range(10)
>>> d['1'][1] = 'hello'
>>> data_string = str(d)
>>> print data_string
0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], '1': 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 1: 'hello'
>>> from ast import literal_eval
>>> d == literal_eval(data_string)
True
一个好处是序列化的数据只是 python 代码,因此非常人性化。将其与您使用pickle.dumps
得到的结果进行比较:
>>> import pickle
>>> print pickle.dumps(d)
(dp0
I0
(lp1
I0
aI1
aI2
aI3
aI4
aI5
aI6
aI7
aI8
aI9
asS'1'
p2
(dp3
I0
(lp4
I0
aI1
aI2
aI3
aI4
aI5
aI6
aI7
aI8
aI9
asI1
S'hello'
p5
ss.
不利的一面是,一旦数据包含 literal_ast
不支持的类型,您就必须转换为其他内容,例如酸洗。
【讨论】:
literal_ast 在我看来无法编码用户定义类的实例,即使是最简单的类; @georg(即pyYAML)提到的解决方案可以做到这一点并产生人类可读的序列化。来自 pypi.org 项目主页(2021 年 6 月):YAML is a data serialization format designed for human readability and interaction with scripting languages. PyYAML is a YAML parser and emitter for Python.
【参考方案6】:
如果您完全信任该字符串并且不关心python injection attacks,那么这是非常简单的解决方案:
d = 'method' : "eval", 'safe' : False, 'guarantees' : None
s = str(d)
d2 = eval(s)
for k in d2:
print k+"="+d2[k]
如果您更有安全意识,那么ast.literal_eval
是更好的选择。
【讨论】:
老实说,这是我一直使用的方法。感谢分享安全提示。如果字典包含可以由 repr 字符串初始化的自定义对象,我使用 repr 而不是 str 你应该默认使用ast.literal_eval
。 eval
的附加值为零,存在很大的安全问题。
糟糕的事情发生了,因为人们真诚地认为在他们的特定代码和平中没有安全问题,所以他们可以快乐地eval
离开。我只是每次都感到厌恶,有人宣扬这种马虎的文化。只需使用json.dumps
和json.loads
(或任何其他非eval
解决方案),没有真正的理由不这样做【参考方案7】:
如果您只尝试序列化,那么 pprint 也可能是一个不错的选择。它需要序列化的对象和文件流。
这里有一些代码:
from pprint import pprint
my_dict = 1:'a',2:'b'
with open('test_results.txt','wb') as f:
pprint(my_dict,f)
我不确定我们是否可以轻松反序列化。我之前使用 json 进行序列化和反序列化,这在大多数情况下都能正常工作。
f.write(json.dumps(my_dict, sort_keys = True, indent = 2, ensure_ascii=True))
但是,在一种特殊情况下,将非 unicode 数据写入 json 时出现了一些错误。
【讨论】:
【参考方案8】:使用 Python 的 json 模块,如果您没有 python 2.6 或更高版本,请使用 simplejson。
【讨论】:
+1:json 比 pickle 好得多,可以以同样的方式使用:json.dumps(mydict)
和 json.loads(mystring)
但是 json 只能处理字符串、数字、列表和字典,而 pickle 可以处理任何 python 类型,但是 json 对于它可以处理的类型比 pickle 更便携
使用json.dumps()
时,请注意某些类型(False
、True
和None
),因为它们与json
不兼容【参考方案9】:
虽然不是严格的序列化,但 json 在这里可能是合理的方法。只要您的数据“简单”,这将处理嵌套的字典和列表以及数据:字符串和基本数字类型。
【讨论】:
以上是关于如何将 Python 字典序列化为字符串,然后再返回字典?的主要内容,如果未能解决你的问题,请参考以下文章
python 将SQLAlchemy Model序列化为字典(用于JSON输出)并从字典属性更新Model。