6

Как красиво вывести вложенные словари?

39

Как я могу красиво отформатировать словарь глубиной около 4 в Python? Я пробовал использовать функцию pprint(), но она не сработала:

import pprint 
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(mydict)

Я хочу, чтобы каждое уровень вложенности имел табуляцию ("\t"), чтобы получить что-то вроде этого:

key1
    value1
    value2
    key2
        value1
        value2

и так далее.

Как это сделать?

5 ответ(ов)

9

Сначала я подумал, что JSON-сериализатор, вероятно, неплохо справляется с вложенными словарями, так что я решил использовать его:

>>> import json
>>> print(json.dumps({'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}},
...                  sort_keys=True, indent=4))
{
    "a": 2,
    "b": {
        "x": 3,
        "y": {
            "t1": 4,
            "t2": 5
        }
    }
}

Этот пример показывает, как можно легко сериализовать вложенные словари в формате JSON с помощью модуля json в Python. Вы можете использовать параметр sort_keys=True, чтобы отсортировать ключи, и indent=4, чтобы отформатировать вывод с отступами, что делает его более читаемым.

2

Не уверен, как именно вы хотите, чтобы выглядел форматирование, но вы можете начать с такой функции:

def pretty(d, indent=0):
   for key, value in d.items():
      print('\t' * indent + str(key))
      if isinstance(value, dict):
         pretty(value, indent+1)
      else:
         print('\t' * (indent+1) + str(value))

Эта функция принимает словарь и рекурсивно обходит его, выводя ключи и значения с соответствующим отступом для вложенных словарей. Вы можете изменить форматирование в зависимости от ваших требований.

0

Вы можете распечатать ваш словарь в красивом виде следующим образом, если имя вашего словаря — yasin:

import json

print(json.dumps(yasin, indent=2))

Либо, что безопаснее, можно воспользоваться таким вариантом:

print(json.dumps(yasin, indent=2, default=str))

Первый способ позволяет форматировать вывод с отступами, делая его более читаемым. Второй способ добавляет аргумент default=str, который позволяет избежать ошибок, если в словаре есть неопознанные типы данных, преобразуя их в строки.

0

На данный момент, я не столкнулся с ни одним "красивым" принтером, который хотя бы как-то имитировал бы вывод интерпретатора Python с простым форматированием, поэтому вот мой вариант:

class Formatter(object):
    def __init__(self):
        self.types = {}
        self.htchar = '\t'
        self.lfchar = '\n'
        self.indent = 0
        self.set_formater(object, self.__class__.format_object)
        self.set_formater(dict, self.__class__.format_dict)
        self.set_formater(list, self.__class__.format_list)
        self.set_formater(tuple, self.__class__.format_tuple)

    def set_formater(self, obj, callback):
        self.types[obj] = callback

    def __call__(self, value, **args):
        for key in args:
            setattr(self, key, args[key])
        formater = self.types.get(type(value), self.types[object])
        return formater(self, value, self.indent)

    def format_object(self, value, indent):
        return repr(value)

    def format_dict(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + repr(key) + ': ' +
            (self.types.get(type(value[key]), self.types[object]))(self, value[key], indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_list(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types.get(type(item), self.types[object]))(self, item, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_tuple(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types.get(type(item), self.types[object]))(self, item, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + self.lfchar + self.htchar * indent)

Чтобы инициализировать форматтер, используйте:

pretty = Formatter()

Вы можете добавить форматтеры для определенных типов, просто создайте для этого функцию, как показано ниже, и привяжите её к нужному типу с помощью метода set_formater:

from collections import OrderedDict

def format_ordereddict(self, value, indent):
    items = [
        self.lfchar + self.htchar * (indent + 1) +
        "(" + repr(key) + ', ' + (self.types.get(type(value[key]), self.types[object]))(self, value[key], indent + 1) + ")"
        for key in value
    ]
    return 'OrderedDict([%s])' % (','.join(items) +
           self.lfchar + self.htchar * indent)
pretty.set_formater(OrderedDict, format_ordereddict)

По историческим причинам, я оставил предыдущий "красивый" принтер, который был функцией вместо класса, но оба варианта можно использовать одинаково, версия с классом просто позволяет делать гораздо больше:

def pretty(value, htchar='\t', lfchar='\n', indent=0):
    nlch = lfchar + htchar * (indent + 1)
    if isinstance(value, dict):
        items = [
            nlch + repr(key) + ': ' + pretty(value[key], htchar, lfchar, indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + lfchar + htchar * indent)
    elif isinstance(value, list):
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + lfchar + htchar * indent)
    elif isinstance(value, tuple):
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + lfchar + htchar * indent)
    else:
        return repr(value)

Чтобы использовать его:

>>> a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':pretty,'unicode':u'\xa7',("tuple","key"):"valid"}
>>> a
{'function': <function pretty at 0x7fdf555809b0>, 'tuple': ('a', 'b', 1, 2), 'list': ['a', 'b', 1, 2], 'dict': {'a': 1, 2: 'b'}, 'unicode': u'\xa7', ('tuple', 'key'): 'valid'}
>>> print(pretty(a))
{
    'function': <function pretty at 0x7fdf555809b0>,
    'tuple': (
        'a',
        'b',
        1,
        2
    ),
    'list': [
        'a',
        'b',
        1,
        2
    ],
    'dict': {
        'a': 1,
        2: 'b'
    },
    'unicode': u'\xa7',
    ('tuple', 'key'): 'valid'
}

По сравнению с другими версиями:

  • Это решение напрямую проверяет тип объекта, так что вы можете "красиво" распечатать почти всё, а не только списки или словари.
  • Не имеет зависимостей.
  • Всё помещается в строку, так что вы можете делать с этим что угодно.
  • Как класс, так и функция были протестированы и работают с Python 2.7 и 3.4.
  • Вы можете иметь любые типы объектов внутри, выводится их представление, а не содержимое (т.е. строки обрамляются кавычками, юникод-строки полностью представлены и т.д.).
  • В версии с классом вы можете добавлять форматирование для любого типа объекта или изменять уже определенные.
  • Ключи могут иметь любой допустимый тип.
  • Символы отступа и новой строки могут быть изменены на любые желаемые.
  • Словари, списки и кортежи выводятся в "красивом" формате.
0

Чтобы решить вашу проблему с ошибкой типа TypeError: Object of type 'datetime' is not JSON serializable, вам нужно передать параметр default, который укажет, как обрабатывать объекты, не поддающиеся сериализации в JSON. Вы можете сделать это, передав default=str, следующим образом:

print(json.dumps(my_dictionary, indent=4, default=str))

Если вы также хотите отсортировать ключи словаря перед сериализацией, вы можете использовать параметр sort_keys:

print(json.dumps(my_dictionary, sort_keys=True, indent=4, default=str))

Таким образом, вы сможете успешно справиться с сериализацией словаря, содержащего значения типа datetime.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь