6

If I have dictionary like:

{
  "cats": {
           "sphinx": 3,
           "british": 2
          },
  "dogs": {}
}

And try to save it to a text file, I get something like this:

{"cats": {"sphinx": 3}, {"british": 2}, "dogs": {}}

How can I save a dictionary in pretty format, so it will be easy to read by human eye?

Anthon
  • 69,918
  • 32
  • 186
  • 246
PinkiNice
  • 1,455
  • 2
  • 13
  • 19

4 Answers4

8

You can import json and specify an indent level:

import json

d = {
  "cats": {
           "sphinx": 3,
           "british": 2
          },
  "dogs": {}
}

j = json.dumps(d, indent=4)
print(j)
{
    "cats": {
        "sphinx": 3, 
        "british": 2
    }, 
    "dogs": {}
}

Note that this is a string, however:

>>> j
'{\n    "cats": {\n        "sphinx": 3, \n        "british": 2\n    }, \n    "dogs": {}\n}'
Alexander
  • 105,104
  • 32
  • 201
  • 196
3

You can use pprint for that:

import pprint
pprint.pformat(thedict)
Vader
  • 3,675
  • 23
  • 40
3

If you want to save it in a more standard format, you can also use, for example, a yaml file (and the related python package http://pyyaml.org/wiki/PyYAMLDocumentation), and the code would look like:

import yaml
dictionary = {"cats": {"sphinx": 3}, {"british": 2}, "dogs": {}}
with open('dictionary_file.yml', 'w') as yaml_file:
     yaml.dump(dictionary, stream=yaml_file, default_flow_style=False)

dump creates a string in the yaml format to be written to the file. Note that it is possible to specify the stream and write the content immediately to the file. If it is necessary to get the string for some reason before writing to the file, just don't specify it and write it after using write function for the file. Note also that the parameter default_flow_style allows to have a nicer format; in the example the file looks:

cats:
  british: 2
  sphinx: 3
dogs: {}

To load again the yaml file in a dictionary:

import yaml
with open('dictionary_file.yml', 'r') as yaml_file:
    dictionary = yaml.load(yaml_file)
albertoql
  • 498
  • 2
  • 6
  • I like the YAML format, but the way you use it, you have the yaml library write the contents to a fake file, then retrieve the contents from that fake file only to write it to a real file. Why don't you write it directly to the real file and prevent this inefficiency? – Anthon Mar 19 '16 at 14:09
  • I'm not using a fake file. The first code in the answer writes in an actual file, the second one reads from an actual file. The with statement provided in python allows to open the file named dictionary_file.yml and the file object can be accessed with the variable yaml_file, and at the end of the with block it is closed without the need of calling the close() function for the file. Check Section 7.2.1 of https://docs.python.org/2/tutorial/inputoutput.html. – albertoql Mar 19 '16 at 15:03
  • You don't seem to know how PyYAML works. If you don't specify a `stream` argument it first write the whole dump to a fake file in memory (StringIO). – Anthon Mar 19 '16 at 17:07
  • Oh sorry, I misunderstood your point. You're right, it is possible to specify directly the stream where to write the whole dump. For the people who don't know PyYAML I just wanted to decouple the dump and the write just for showing in a more clear way what's going on and that it's possible to get the string. Anyway, if everybody is clear on that, I modify the answer accordingly. – albertoql Mar 19 '16 at 17:14
0

You can dump it by using the Python Object Notation module (pon: disclaimer I am the author of that module)

from pon import PON, loads

data = {
    "cats": {
        "sphinx": 3,
        "british": 2
        },
    "dogs": {}
}

pon = PON(obj=data)
pon.dump()

which gives:

dict(
    cats=dict(
        sphinx=3,
        british=2,
    ),
    dogs=dict(    ),
)

which again is correct Python, but trading the quoted strings needed for keys by using dict .

You can load this again with:

read_back = loads(open('file_name.pon').read())
print(read_back)

giving:

{'cats': {'sphinx': 3, 'british': 2}, 'dogs': {}}

Please note that loads() does not evaluate the string, it actually parses it safely using python's built-in parser.

PON also allows you to load python dictionaries from files, that have commented entries, and dump them while preserving the comments. This is where it's real usefulness comes into action.


Alternatively, if you would like something, arbitrarily more readable like the YAML format, you can use ruamel.yaml and do:

import ruamel.yaml
ruamel.yaml.round_trip_dump(data, stream=open('file_name.yaml', 'wb'), indent=4) 

which gives you a file file_name.yaml with contents:

cats:
    sphinx: 3
    british: 2
dogs: {}

which uses the indent you seem to prefer (and is more efficient than @alberto's version)

Anthon
  • 69,918
  • 32
  • 186
  • 246