如何以人类可读的格式序列化Python对象?

29

我需要将由列表/字典/元组等Python结构制成的内容以人类可读格式进行存储。我的想法是使用类似于pickle的东西,但pickle不友好。其他我想到的选择包括YAML(通过PyYAML)和JSON(通过simplejson)序列化器。

还有其他你能想到的选项吗?


序列化意味着您可以使用保存的输出重新实例化,这是大多数答案没有展示的。您正在特别寻求内置类型的可读序列化,这更容易。使用repr()对变量进行序列化,然后使用ast.literal_eval将其加载回来。例如,您可以进行一个快速测试:import ast; ast.literal_eval(repr(myvariable)),例如myvariable=(1,2,3)。如果您的列表/字典/元组包含非内置类型,则逻辑上应该将它们保存在可读的*.py*模块中,该模块定义了类型并加载值,因为您还需要类型实现。 - Rian Rizvi
7个回答

19

对于简单的情况,pprint() 和 eval() 可以考虑。

使用您的示例:

>>> d = {'age': 27,
...  'name': 'Joe',
...  'numbers': [1, 
...              2, 
...              3,
...              4,
...              5],
...  'subdict': {
...              'first': 1, 
...              'second': 2,
...               'third': 3
...              }
... }
>>> 
>>> from pprint import pprint
>>> pprint(d)
{'age': 27,
 'name': 'Joe',
 'numbers': [1, 2, 3, 4, 5],
 'subdict': {'first': 1, 'second': 2, 'third': 3}}
>>> 

我会认真考虑一下是否应该使用同一个工具来解决两个需求。您是否考虑过使用pickle进行序列化,然后使用pprint()(或更高级的对象查看器)来供人类查看对象?


3
我得把那些数据结构存储到文件中,这样程序才能将其加载/保存。但我也希望它们能被人类编辑/阅读。 - pistacchio

15
如果只使用Python的列表、字典和元组对象,那么使用JSON是最好的选择。它易于阅读、操作,并且与语言无关。
注意:在simplejson中,元组将被转换为列表。
In [109]: simplejson.loads(simplejson.dumps({'d':(12,3,4,4,5)}))
Out[109]: {u'd': [12, 3, 4, 4, 5]}

1
2.2.0版本新增了tuple_as_array标志来更改simplejson处理元组的方式。请参见http://simplejson.readthedocs.org/en/latest/index.html#encoders-and-decoders,以及原始更改https://github.com/simplejson/simplejson/pull/6。这使得我们可以保留元组和列表之间的差异。 - jomido

8
你应该查看jsonpickle (https://github.com/jsonpickle/jsonpickle)。它可以将任何python对象写入json文件,然后将该文件读回到python对象中。好处是中间文件非常易读,因为它是json格式的。

1
jsonpickle已经迁移到https://github.com/jsonpickle/jsonpickle,您今天仍然推荐使用它吗? - Hugo Trentesaux

4

你是什么意思,这不是人类可读的吗??? ;)

>>> d = {'age': 27, 
...   'name': 'Joe',
...   'numbers': [1,2,3,4,5],
...   'subdict': {'first':1, 'second':2, 'third':3}
... }
>>> 
>>> import pickle
>>> p = pickle.dumps(d)      
>>> p
"(dp0\nS'age'\np1\nI27\nsS'subdict'\np2\n(dp3\nS'second'\np4\nI2\nsS'third'\np5\nI3\nsS'first'\np6\nI1\nssS'name'\np7\nS'Joe'\np8\nsS'numbers'\np9\n(lp10\nI1\naI2\naI3\naI4\naI5\nas."

好的,也许只需要一些练习……或者你可以作弊。

>>> import pickletools 
>>> pickletools.dis(p)
    0: (    MARK
    1: d        DICT       (MARK at 0)
    2: p    PUT        0
    5: S    STRING     'age'
   12: p    PUT        1
   15: I    INT        27
   19: s    SETITEM
   20: S    STRING     'subdict'
   31: p    PUT        2
   34: (    MARK
   35: d        DICT       (MARK at 34)
   36: p    PUT        3
   39: S    STRING     'second'
   49: p    PUT        4
   52: I    INT        2
   55: s    SETITEM
   56: S    STRING     'third'
   65: p    PUT        5
   68: I    INT        3
   71: s    SETITEM
   72: S    STRING     'first'
   81: p    PUT        6
   84: I    INT        1
   87: s    SETITEM
   88: s    SETITEM
   89: S    STRING     'name'
   97: p    PUT        7
  100: S    STRING     'Joe'
  107: p    PUT        8
  110: s    SETITEM
  111: S    STRING     'numbers'
  122: p    PUT        9
  125: (    MARK
  126: l        LIST       (MARK at 125)
  127: p    PUT        10
  131: I    INT        1
  134: a    APPEND
  135: I    INT        2
  138: a    APPEND
  139: I    INT        3
  142: a    APPEND
  143: I    INT        4
  146: a    APPEND
  147: I    INT        5
  150: a    APPEND
  151: s    SETITEM
  152: .    STOP
highest protocol among opcodes = 0
>>> 

但是,您仍然需要从文件中读取被pickled的对象,但是您不需要进行load。因此,如果它是一个“危险”的对象,则在执行load之前仍可能能够弄清楚这一点。如果您被困在pickle中,则使用它来解密您的内容可能是一个不错的选择。


4

如果你需要比JSON更多的表示形式,我强烈推荐查看PyON(Python对象表示法)......尽管我相信它仅限于2.6 / 3.0及以上版本,因为它依赖于ast模块。它处理自定义类实例和递归数据类型等其他功能,这比JSON提供的要多。


2

要使用simplejson,首先需要进行 easy_install simplejson 安装:

import simplejson
my_structure = {"name":"Joe", "age":27, "numbers":[1,2,3,4,5], "subdict":{"first":1, "second":2, "third": 3}}
json = simplejson.dumps(my_structure)

转化为JSON后的结果为:

{"age": 27, "subdict": {"second": 2, "third": 3, "first": 1}, "name": "Joe", "numbers": [1, 2, 3, 4, 5]}

请注意,它几乎没有改变字典的格式,但您应该通过此步骤运行它以确保有效的JSON数据。
您可以进一步美化打印结果:
import pprint
pprint.pprint(my_structure)

结果是:

{'age': 27,
 'name': 'Joe',
 'numbers': [1, 2, 3, 4, 5],
 'subdict': {'first': 1, 'second': 2, 'third': 3}}

0

AXON(文本)格式,结合了JSON、XML和YAML的最佳特点。AXON格式相当易读且相对紧凑。

Python (2.7/3.3-3.7)模块pyaxon支持load(s)/dump(s)功能,包括迭代loading/dumping。速度足够快以便于使用。

考虑简单的例子:

>>> d = {
     'age': 27, 'name': 'Joe', 
     'numbers': [1, 2, 3, 4, 5], 
     'subdict': {'first': 1, 'second': 2, 'third': 3}
    }
# pretty form
>>> axon.dumps(d, pretty=1)
{ age: 27
  name: "Joe"
  numbers: [1 2 3 4 5]
  subdict: {
    first: 1
    second: 2
    third: 3}}
# compact form
>>> axon.dumps(d)
{age:27 name:"Joe" numbers:[1 2 3 4 5] subdict:{first:1 second:2 third:3}}

它还可以处理消息中的多个对象:

>>> msg = axon.dumps([{'a':1, 'b':2, 'c':3}, {'a':2, 'b':3, 'c':4}])
>>> print(msg)
{a:1 b:2 c:3} 
{a:2 b:3 c:4}
{a:3 b:4 c:5}

然后迭代加载它们:

for d in axon.iloads(msg):
   print(d)

3
Bitbucket的链接已经失效了(感谢Atlassian)。 - Fake Name

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接