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

Как я могу напечатать словарь с глубиной ~ 4 в Python? Я попробовал довольно печатать с помощью pprint() , но это не сработало:

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

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

 key1 value1 value2 key2 value1 value2 

и т.п.

Как я могу это сделать?

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

 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)) 

( для пользователя python 2: импортируйте функцию печати из __future__ )

Моя первая мысль заключалась в том, что сериализатор 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 } } } 

Что касается того, что было сделано, я не вижу симпатичного принтера, который, по крайней мере, имитирует вывод интерпретатора 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[type(value) if type(value) in self.types else 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[type(value[key]) if type(value[key]) in self.types else 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[type(item) if type(item) in self.types else 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[type(item) if type(item) in self.types else object])(self, item, indent + 1) for item in value ] return '(%s)' % (','.join(items) + self.lfchar + self.htchar * indent) 

Чтобы инициализировать его:

 pretty = Formatter() 

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

 from collections import OrderedDict def format_ordereddict(self, value, indent): items = [ self.lfchar + self.htchar * (indent + 1) + "(" + repr(key) + ', ' + (self.types[ type(value[key]) if type(value[key]) in self.types else 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) 

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

 def pretty(value, htchar='\t', lfchar='\n', indent=0): nlch = lfchar + htchar * (indent + 1) if type(value) is dict: items = [ nlch + repr(key) + ': ' + pretty(value[key], htchar, lfchar, indent + 1) for key in value ] return '{%s}' % (','.join(items) + lfchar + htchar * indent) elif type(value) is list: items = [ nlch + pretty(item, htchar, lfchar, indent + 1) for item in value ] return '[%s]' % (','.join(items) + lfchar + htchar * indent) elif type(value) is 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': , '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': , 'tuple': ( 'a', 'b', 1, 2 ), 'list': [ 'a', 'b', 1, 2 ], 'dict': { 'a': 1, 2: 'b' }, 'unicode': u'\xa7', ('tuple', 'key'): 'valid' } 

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

  • Это решение выглядит напрямую для типа объекта, поэтому вы можете печатать почти все, а не только список или dict.
  • Не имеет никакой зависимости.
  • Все помещается внутри строки, поэтому вы можете делать с ней все, что хотите.
  • Класс и функция были протестированы и работают с Python 2.7 и 3.4.
  • Вы можете иметь все типы объектов внутри, это их представления, а не их содержимое, которое помещается в результат (так что строки имеют кавычки, строка Unicode полностью представлена ​​…).
  • С версией classа вы можете добавить форматирование для каждого типа объекта, который вы хотите, или изменить его для уже определенных.
  • ключ может быть любого допустимого типа.
  • Отступ и символ новой строки могут быть изменены для всего, что нам хотелось бы.
  • Dict, List и Suples довольно печатаются.

Вы можете попробовать YAML через PyYAML . Его выход может быть точно настроен. Я предлагаю начать со следующего:

print yaml.dump(data, allow_unicode=True, default_flow_style=False)

Результат очень читабельен; его можно также проанализировать на Python, если это необходимо.

Редактировать:

Пример:

 >>> import yaml >>> data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}} >>> print yaml.dump(data, default_flow_style=False) a: 2 b: x: 3 y: t1: 4 t2: 5 

Я принял ответ sth и немного изменил его, чтобы соответствовать моим потребностям вложенных словарей и списков:

 def pretty(d, indent=0): if isinstance(d, dict): for key, value in d.iteritems(): print '\t' * indent + str(key) if isinstance(value, dict) or isinstance(value, list): pretty(value, indent+1) else: print '\t' * (indent+1) + str(value) elif isinstance(d, list): for item in d: if isinstance(item, dict) or isinstance(item, list): pretty(item, indent+1) else: print '\t' * (indent+1) + str(item) else: pass 

Который тогда дает мне вывод как:

 >>> xs:schema @xmlns:xs http://www.w3.org/2001/XMLSchema xs:redefine @schemaLocation base.xsd xs:complexType @name Extension xs:complexContent xs:restriction @base Extension xs:sequence xs:element @name Policy @minOccurs 1 xs:complexType xs:sequence xs:element ... 

Как и другие публикации, вы можете использовать recursion / dfs для печати вложенных данных словаря и рекурсивного вызова, если это словарь; иначе распечатайте данные.

  >>> def print_json(data): ... if type(data) == dict: ... for k,v in data.items(): ... print k ... print_json(v) ... else: ... print data ... 

Sth, я поторопись, это довольно;)

 def pretty(d, indent=0): for key, value in d.iteritems(): if isinstance(value, dict): print '\t' * indent + (("%30s: {\n") % str(key).upper()) pretty(value, indent+1) print '\t' * indent + ' ' * 32 + ('} # end of %s #\n' % str(key).upper()) elif isinstance(value, list): for val in value: print '\t' * indent + (("%30s: [\n") % str(key).upper()) pretty(val, indent+1) print '\t' * indent + ' ' * 32 + ('] # end of %s #\n' % str(key).upper()) else: print '\t' * indent + (("%30s: %s") % (str(key).upper(),str(value))) 
 This class prints out a complex nested dictionary with sub dictionaries and sub lists. ## ## Recursive class to parse and print complex nested dictionary ## class NestedDictionary(object): def __init__(self,value): self.value=value def print(self,depth): spacer="--------------------" if type(self.value)==type(dict()): for kk, vv in self.value.items(): if (type(vv)==type(dict())): print(spacer[:depth],kk) vvv=(NestedDictionary(vv)) depth=depth+3 vvv.print(depth) depth=depth-3 else: if (type(vv)==type(list())): for i in vv: vvv=(NestedDictionary(i)) depth=depth+3 vvv.print(depth) depth=depth-3 else: print(spacer[:depth],kk,vv) ## ## Instatiate and execute - this prints complex nested dictionaries ## with sub dictionaries and sub lists ## 'something' is a complex nested dictionary MyNest=NestedDictionary(weather_com_result) MyNest.print(0) 

Я написал этот простой код для печати общей структуры объекта json в Python.

 def getstructure(data, tab = 0): if type(data) is dict: print ' '*tab + '{' for key in data: print ' '*tab + ' ' + key + ':' getstructure(data[key], tab+4) print ' '*tab + '}' elif type(data) is list and len(data) > 0: print ' '*tab + '[' getstructure(data[0], tab+4) print ' '*tab + ' ...' print ' '*tab + ']' 

результат для следующих данных

 a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':'p','unicode':u'\xa7',("tuple","key"):"valid"} getstructure(a) 

очень компактна и выглядит следующим образом:

 { function: tuple: list: [ ... ] dict: { a: 2: } unicode: ('tuple', 'key'): } 

Вот что-то, что будет печатать любой тип вложенного словаря, при этом отслеживая «родительские» словари на этом пути.

 dicList = list() def prettierPrint(dic, dicList): count = 0 for key, value in dic.iteritems(): count+=1 if str(value) == 'OrderedDict()': value = None if not isinstance(value, dict): print str(key) + ": " + str(value) print str(key) + ' was found in the following path:', print dicList print '\n' elif isinstance(value, dict): dicList.append(key) prettierPrint(value, dicList) if dicList: if count == len(dic): dicList.pop() count = 0 prettierPrint(dicExample, dicList) 

Это хорошая отправная точка для печати в соответствии с различными форматами, например, указанными в OP. Все, что вам действительно нужно сделать, это операции вокруг блоков печати . Обратите внимание, что он видит, стоит ли значение «OrderedDict ()». В зависимости от того, используете ли вы что-то из коллекций данных Container Collections , вы должны сделать такие отказоустойчивые системы, чтобы блок elif не рассматривал его как дополнительный словарь из-за его имени. На данный момент, например, словарь

 example_dict = {'key1': 'value1', 'key2': 'value2', 'key3': {'key3a': 'value3a'}, 'key4': {'key4a': {'key4aa': 'value4aa', 'key4ab': 'value4ab', 'key4ac': 'value4ac'}, 'key4b': 'value4b'} 

распечатает

 key3a: value3a key3a was found in the following path: ['key3'] key2: value2 key2 was found in the following path: [] key1: value1 key1 was found in the following path: [] key4ab: value4ab key4ab was found in the following path: ['key4', 'key4a'] key4ac: value4ac key4ac was found in the following path: ['key4', 'key4a'] key4aa: value4aa key4aa was found in the following path: ['key4', 'key4a'] key4b: value4b key4b was found in the following path: ['key4'] 

~ изменение кода для соответствия формату вопроса ~

 lastDict = list() dicList = list() def prettierPrint(dic, dicList): global lastDict count = 0 for key, value in dic.iteritems(): count+=1 if str(value) == 'OrderedDict()': value = None if not isinstance(value, dict): if lastDict == dicList: sameParents = True else: sameParents = False if dicList and sameParents is not True: spacing = ' ' * len(str(dicList)) print dicList print spacing, print str(value) if dicList and sameParents is True: print spacing, print str(value) lastDict = list(dicList) elif isinstance(value, dict): dicList.append(key) prettierPrint(value, dicList) if dicList: if count == len(dic): dicList.pop() count = 0 

Используя тот же примерный код, он напечатает следующее:

 ['key3'] value3a ['key4', 'key4a'] value4ab value4ac value4aa ['key4'] value4b 

Это не совсем то , что запрашивается в OP. Разница заключается в том, что родительский ^ n все еще печатается, вместо того, чтобы отсутствовать и заменяется пробелом. Чтобы добраться до формата OP, вам нужно сделать что-то вроде следующего: итеративно сравнить dicList с lastDict . Вы можете сделать это, создав новый словарь и скопировав содержимое dicList, проверяя, является ли i в скопированном словаре тем же, что и i в lastDict, и – если он – пишет пробел в эту позицию i, используя функцию множителя строк ,

По этой ссылке :

 def prnDict(aDict, br='\n', html=0, keyAlign='l', sortKey=0, keyPrefix='', keySuffix='', valuePrefix='', valueSuffix='', leftMargin=0, indent=1 ): ''' return a string representive of aDict in the following format: { key1: value1, key2: value2, ... } Spaces will be added to the keys to make them have same width. sortKey: set to 1 if want keys sorted; keyAlign: either 'l' or 'r', for left, right align, respectively. keyPrefix, keySuffix, valuePrefix, valueSuffix: The prefix and suffix to wrap the keys or values. Good for formatting them for html document(for example, keyPrefix='', keySuffix=''). Note: The keys will be padded with spaces to have them equally-wide. The pre- and suffix will be added OUTSIDE the entire width. html: if set to 1, all spaces will be replaced with ' ', and the entire output will be wrapped with '' and ''. br: determine the carriage return. If html, it is suggested to set br to '
'. If you want the html source code eazy to read, set br to '
\n' version: 04b52 author : Runsun Pan require: odict() # an ordered dict, if you want the keys sorted. Dave Benjamin http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/161403 ''' if aDict: #------------------------------ sort key if sortKey: dic = aDict.copy() keys = dic.keys() keys.sort() aDict = odict() for k in keys: aDict[k] = dic[k] #------------------- wrap keys with ' ' (quotes) if str tmp = ['{'] ks = [type(x)==str and "'%s'"%x or x for x in aDict.keys()] #------------------- wrap values with ' ' (quotes) if str vs = [type(x)==str and "'%s'"%x or x for x in aDict.values()] maxKeyLen = max([len(str(x)) for x in ks]) for i in range(len(ks)): #-------------------------- Adjust key width k = {1 : str(ks[i]).ljust(maxKeyLen), keyAlign=='r': str(ks[i]).rjust(maxKeyLen) }[1] v = vs[i] tmp.append(' '* indent+ '%s%s%s:%s%s%s,' %( keyPrefix, k, keySuffix, valuePrefix,v,valueSuffix)) tmp[-1] = tmp[-1][:-1] # remove the ',' in the last item tmp.append('}') if leftMargin: tmp = [ ' '*leftMargin + x for x in tmp ] if html: return '%s' %br.join(tmp).replace(' ',' ') else: return br.join(tmp) else: return '{}' ''' Example: >>> a={'C': 2, 'B': 1, 'E': 4, (3, 5): 0} >>> print prnDict(a) { 'C' :2, 'B' :1, 'E' :4, (3, 5):0 } >>> print prnDict(a, sortKey=1) { 'B' :1, 'C' :2, 'E' :4, (3, 5):0 } >>> print prnDict(a, keyPrefix="", keySuffix="") { 'C' :2, 'B' :1, 'E' :4, (3, 5):0 } >>> print prnDict(a, html=1) {  'C'   :2,  'B'   :1,  'E'   :4,  (3, 5):0 } >>> b={'car': [6, 6, 12], 'about': [15, 9, 6], 'bookKeeper': [9, 9, 15]} >>> print prnDict(b, sortKey=1) { 'about' :[15, 9, 6], 'bookKeeper':[9, 9, 15], 'car' :[6, 6, 12] } >>> print prnDict(b, keyAlign="r") { 'car':[6, 6, 12], 'about':[15, 9, 6], 'bookKeeper':[9, 9, 15] } '''

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

 def print_JSON_tree(d, indent=0): for key, value in d.iteritems(): print ' ' * indent + unicode(key), if isinstance(value, dict): print; print_JSON_tree(value, indent+1) else: print ":", str(type(d[key])).split("'")[1], "-", str(len(unicode(d[key]))) 

Это очень приятно, когда у вас большие объекты JSON и вы хотите выяснить, где находится мясо. Пример :

 >>> print_JSON_tree(JSON_object) key1 value1 : int - 5 value2 : str - 16 key2 value1 : str - 34 value2 : list - 5623456 

Это скажет вам, что большинство данных, о которых вы заботитесь, вероятно, JSON_object['key1']['key2']['value2'] внутри JSON_object['key1']['key2']['value2'] потому что длина этого значения, отформатированного как строка, очень велика.

Я сам относительный новичок python, но за последние пару недель я работал с вложенными словарями, и это то, что я придумал.

Вы должны попробовать использовать стек. Сделайте ключи из корневого словаря в список списка:

 stack = [ root.keys() ] # Result: [ [root keys] ] 

Идя в обратном порядке от последнего к первому, найдите каждый ключ в словаре, чтобы узнать, является ли его значение (также) словарем. Если нет, распечатайте ключ и удалите его. Однако, если значение для ключа является словарем, распечатайте ключ, затем добавьте ключи для этого значения в конец стека и начните обработку этого списка таким же образом, повторяя рекурсивно для каждого нового списка ключей.

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

 [['key 1','key 2'],['key 2.1','key 2.2'],['key 2.2.1','key 2.2.2'],[`etc.`]] 

Подход к этому подходу заключается в том, что отступ всего раз превышает длину стека:

 indent = "\t" * len(stack) 

Недостатком является то, что для проверки каждой клавиши вам нужно перейти к соответствующему подъяруску, хотя это можно легко справиться с пониманием списка и простым циклом:

 path = [li[-1] for li in stack] # The last key of every list of keys in the stack sub = root for p in path: sub = sub[p] if type(sub) == dict: stack.append(sub.keys()) # And so on 

Имейте в виду, что для этого подхода вам потребуется очистить конечные пустые списки и удалить последний ключ в любом списке, за которым следует пустой список (который, конечно же, может создать еще один пустой список и т. Д.).

Существуют и другие способы реализации этого подхода, но, надеюсь, это дает вам общее представление о том, как это сделать.

EDIT: Если вы не хотите проходить все это, модуль pprint печатает вложенные словари в хорошем формате.

Вот функция, которую я написал на основе комментария sth. Он работает так же, как json.dumps с отступом, но я использую вкладки вместо пространства для отступов. В Python 3.2+ вы можете указать отступ как «\ t» напрямую, но не в 2.7.

 def pretty_dict(d): def pretty(d, indent): for i, (key, value) in enumerate(d.iteritems()): if isinstance(value, dict): print '{0}"{1}": {{'.format( '\t' * indent, str(key)) pretty(value, indent+1) if i == len(d)-1: print '{0}}}'.format( '\t' * indent) else: print '{0}}},'.format( '\t' * indent) else: if i == len(d)-1: print '{0}"{1}": "{2}"'.format( '\t' * indent, str(key), value) else: print '{0}"{1}": "{2}",'.format( '\t' * indent, str(key), value) print '{' pretty(d,indent=1) print '}' 

Пример:

 >>> dict_var = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}} >>> pretty_dict(dict_var) { "a": "2", "b": { "y": { "t2": "5", "t1": "4" }, "x": "3" } } 
Давайте будем гением компьютера.