Comment imprimer de jolis dictionnaires imbriqués?

289

Comment puis-je imprimer un joli dictionnaire avec une profondeur de ~ 4 en Python? J'ai essayé de jolies impressions avec pprint(), mais cela n'a pas fonctionné:

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

Je veux simplement une indentation ( "\t") pour chaque imbrication, afin d'obtenir quelque chose comme ceci:

key1
    value1
    value2
    key2
       value1
       value2

etc.

Comment puis-je faire ceci?

martineau
la source
29
Que signifie «n'a pas fonctionné»? Veuillez préciser très précisément comment pprint "n'a pas fonctionné".
S.Lott
5
J'ai maintenant utilisé 3 de ces réponses (chacune bonne dans un scénario spécifique): @ La réponse json de Ken est bonne mais échoue parfois lorsque l'objet ne peut pas être json sérialisable (lève une exception). si la réponse json de @ Ken ne fonctionne pas, essayez la réponse yaml de @ Andy et cela devrait fonctionner mais la sortie de la chaîne est un peu moins lisible par l'homme. [La réponse de @ sth] est la plus générique (devrait fonctionner pour n'importe quel objet et n'utilise aucune bibliothèque).
Trevor Boyd Smith

Réponses:

143

Je ne sais pas exactement à quoi vous voulez que le formatage ressemble, mais vous pouvez commencer avec une fonction comme celle-ci:

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))
qch
la source
8
Vous savez que la réponse conventionnelle de @ Ken est bien meilleure que cela. Json gère déjà tout et cela peut donner des erreurs telles que: UnicodeEncodeError: le codec 'ascii' ne peut pas coder le caractère u '\ xf3' en position 50: ordinal pas dans la plage (128)
wonderwhy
Je ne peux pas le faire fonctionner avec le dict imbriqué de ma solution, car il m'a donné une erreur UnicodeEncodeError, il n'imprime pas non plus la clé dict, ne va pas dans la liste et les tuples et ne garde pas de syntaxe python valide.
y.petremann
Cette réponse a fonctionné comme un charme pour moi, mais j'ai posté une nouvelle question stackoverflow.com/questions/36972225/… qui fixe une limite au nombre de valeurs à imprimer.
gsamaras
Assez bien. Si vous avez des listes imbriquées comme dans la question du PO, vous devez ajouter un peu de gestion pour cela. Si vous rencontrez des problèmes dans Py2, c'est parce qu'il ne peut pas gérer correctement Unicode sans des hacks comme __future__celui-ci, la réponse mentionne maintenant, vous devez donc les utiliser là où vous en avez besoin (ou passer à 3 déjà).
sudo
Cela a assez bien fonctionné pour moi: python def pretty(d, indent=0): for key, value in d.items(): if isinstance(value, dict): print(' ' * indent + str(key)) pretty(value, indent+1) else: print(' ' * (indent+1) + f"{key}: {value}")
hum3
500

Ma première pensée a été que le sérialiseur JSON est probablement assez bon pour les dictionnaires imbriqués, donc je tricherais et l'utiliserais:

>>> 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
        }
    }
}
Ken
la source
41
C'est cool, mais n'imprime pas bien tous les dictionnaires. print json.dumps (myObject .__ dict__, sort_keys = True, indent = 4) #TypeError: <objet à 0x0000000002E6A748> n'est pas sérialisable JSON
tponthieux
4
Bien que cela semble utile, sa sortie n'est pas ce que l'OP voulait.
martineau
2
@martineau: La sortie demandée par l'OP n'a pas de sens, les dictionnaires ont besoin de clés par valeur.
naught101
2
@ naught101: Une jolie imprimante peut faire tout ce qui est nécessaire pour produire la sortie souhaitée.
martineau
22
json.dumps prend une fonction de conversion comme argument facultatif, donc avec json.dumps (myObject .__ dict__, sort_keys = True, indent = 4, deault = str) vous pouvez au moins utiliser une implémentation d'objets de repr pour s'imprimer et contourner le typeError «non JSON sérialisable»
RFairey
56

Vous pouvez essayer YAML via PyYAML . Sa sortie peut être affinée. Je suggère de commencer par ce qui suit:

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

Le résultat est très lisible; il peut également être analysé en Python si nécessaire.

Éditer:

Exemple:

>>> 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
Andy Mikhaylenko
la source
1
Utiliser yaml est très intéressant car il garde le type de données sur son format, la seule chose que je peux dire contre, c'est qu'il ne produit pas de chaîne python valide, mais peut presque être reconverti en python.
y.petremann
1
yaml n'aime pas la version Numpy des types scalaires ... Je n'ai pas été surpris qu'il ne supporte pas les tableaux numpy, mais je m'attendais à la même sortie pour a floatet anumpy.float64
PhilMacKay
cette approche a également fonctionné pour moi en utilisant une liste de dictionnaires
Grant Shannon
36

D'après ce qui a été fait, je ne vois aucune jolie imprimante qui imite au moins la sortie de l'interpréteur python avec un formatage très simple alors voici le mien:

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)

Pour l'initialiser:

pretty = Formatter()

Il peut prendre en charge l'ajout de formateurs pour les types définis, il vous suffit de créer une fonction pour celle-ci comme celle-ci et de la lier au type souhaité avec 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)

Pour des raisons historiques, je garde la jolie imprimante précédente qui était une fonction au lieu d'une classe, mais elles peuvent toutes les deux être utilisées de la même manière, la version de classe permet simplement beaucoup plus:

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)

Pour l'utiliser :

>>> 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'
}

Par rapport aux autres versions:

  • Cette solution recherche directement le type d'objet, vous pouvez donc imprimer à peu près tout, pas seulement la liste ou la dictée.
  • N'a pas de dépendance.
  • Tout est placé dans une chaîne, vous pouvez donc en faire ce que vous voulez.
  • La classe et la fonction ont été testées et fonctionnent avec Python 2.7 et 3.4.
  • Vous pouvez avoir tout type d'objets à l'intérieur, ce sont leurs représentations et non leur contenu qui est mis dans le résultat (donc la chaîne a des guillemets, la chaîne Unicode est entièrement représentée ...).
  • Avec la version de classe, vous pouvez ajouter une mise en forme pour chaque type d'objet que vous souhaitez ou les modifier pour ceux déjà définis.
  • La clé peut être de n'importe quel type valide.
  • Le retrait et le caractère de nouvelle ligne peuvent être modifiés pour tout ce que nous souhaitons.
  • Dict, List et Tuples sont joliment imprimés.
y.petremann
la source
2
Cela devrait certainement être la solution acceptée - le manque de dépendance à JSON est énorme.
Josh
ce serait cool s'il pouvait faire des objets en les convertissant en dict et en définissant leur clé comme type d'objet
Alex Cory
Vous pouvez essentiellement remplacer la méthode format_object en interne ou en externe pour ce faire.
y.petremann
set_formater - besoin de deux t, ceci est une faute de frappe, devrait être formateur
Nikolay Prokopyev
32

de cette façon, vous pouvez l'imprimer de manière jolie, par exemple le nom de votre dictionnaire est yasin

import json

print (json.dumps(yasin, indent=2))
yasin lachini
la source
5
Cela suppose que le contenu du dictionnaire est json sérialisable, ce qui n'est pas nécessairement vrai.
SpiXel
8

Une autre option avec yapf:

from pprint import pformat
from yapf.yapflib.yapf_api import FormatCode

dict_example = {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5], '4': {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5]}}
dict_string = pformat(dict_example)
formatted_code, _ = FormatCode(dict_string)

print(formatted_code)

Production:

{
    '1': '1',
    '2': '2',
    '3': [1, 2, 3, 4, 5],
    '4': {
        '1': '1',
        '2': '2',
        '3': [1, 2, 3, 4, 5]
    }
}
Eyal Levin
la source
5

Comme d'autres l'ont publié, vous pouvez utiliser récursivité / dfs pour imprimer les données du dictionnaire imbriqué et appeler de manière récursive s'il s'agit d'un dictionnaire; sinon imprimez les données.

def print_json(data):
    if type(data) == dict:
            for k, v in data.items():
                    print k
                    print_json(v)
    else:
            print data
Rohit Malgaonkar
la source
5

la moue peut imprimer tout ce que vous lui lancez, par exemple (en empruntant dataà une autre réponse):

data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
pout.vs(data)

entraînerait une sortie imprimée à l'écran comme:

{
    'a': 2,
    'b':
    {
        'y':
        {
            't2': 5,
            't1': 4
        },
        'x': 3
    }
}

ou vous pouvez renvoyer la sortie de chaîne formatée de votre objet:

v = pout.s(data)

Son cas d'utilisation principal est pour le débogage afin qu'il ne s'étouffe pas sur les instances d'objets ou quoi que ce soit et qu'il gère la sortie Unicode comme vous vous en doutez, fonctionne en python 2.7 et 3.

divulgation : je suis l'auteur et le mainteneur de la moue.

Jaymon
la source
5

L'un des moyens les plus pythoniques pour cela est d'utiliser le module pprint déjà construit .

L'argument dont vous avez besoin pour définir la profondeur d'impression est celui auquel vous vous attendez depth

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

C'est tout !

Juan-Kabbali
la source
3

J'ai pris la réponse de sth et l'ai légèrement modifiée pour l'adapter à mes besoins de dictionnaires et de listes imbriqués:

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

Ce qui me donne alors une sortie comme:

>>> 
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
                                            ...
Jamie Ivanov
la source
1

Sth, je coule c'est joli;)

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)))
VindeX
la source
1
-1: Ne gère pas les listvaleurs qui ne sont pas des dictinstances, c'est-à-dire pretty({'key': [1, 2, 3]}, indent=4)==> AttributeError: 'int' object has no attribute 'iteritems'. Je n'aime pas non plus les majuscules.
martineau
Votre solution considère qu'il ne peut pas y avoir de dict à l'intérieur d'une liste à l'intérieur du dict racine. Il considère également que nous ne voulons pas imprimer une liste ou un tuple. Enfin, ne mettez pas les clés en majuscule, le résultat pour {'a': 0, 'A': 1} ne serait pas correct.
y.petremann
1
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)
Bob Lockwood
la source
1

J'ai écrit ce code simple pour imprimer la structure générale d'un objet json en 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 + ']'

le résultat pour les données suivantes

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)

est très compact et ressemble à ceci:

{
  function:
  tuple:
  list:
    [
      ...
    ]
  dict:
    {
      a:
      2:
    }
  unicode:
  ('tuple', 'key'):
}
Abtin Rasoulian
la source
0

Je suis moi-même un débutant en python, mais je travaille avec des dictionnaires imbriqués depuis quelques semaines et c'est ce que j'avais trouvé.

Vous devriez essayer d'utiliser une pile. Transformez les clés du dictionnaire racine en une liste de liste:

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

En allant dans l'ordre inverse du dernier au premier, recherchez chaque clé dans le dictionnaire pour voir si sa valeur est (également) un dictionnaire. Sinon, imprimez la clé puis supprimez-la. Cependant, si la valeur de la clé est un dictionnaire, imprimez la clé, puis ajoutez les clés de cette valeur à la fin de la pile et commencez à traiter cette liste de la même manière, en répétant de manière récursive pour chaque nouvelle liste de clés.

Si la valeur de la deuxième clé de chaque liste était un dictionnaire, vous auriez quelque chose comme ceci après plusieurs tours:

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

L'avantage de cette approche est que le retrait est juste \tmultiplié par la longueur de la pile:

indent = "\t" * len(stack)

L'inconvénient est que pour vérifier chaque clé, vous devez hacher le sous-dictionnaire correspondant, bien que cela puisse être géré facilement avec une compréhension de la liste et une forboucle simple :

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

N'oubliez pas que cette approche vous obligera à nettoyer les listes vides de fin et à supprimer la dernière clé de toute liste suivie d'une liste vide (ce qui peut bien sûr créer une autre liste vide, etc.).

Il existe d'autres façons de mettre en œuvre cette approche, mais j'espère que cela vous donne une idée de base de la façon de le faire.

EDIT: Si vous ne voulez pas passer par tout cela, le pprintmodule imprime les dictionnaires imbriqués dans un format agréable.

danwroy
la source
0

Voici une fonction que j'ai écrite sur la base du commentaire de qc. Cela fonctionne de la même manière que json.dumps avec le retrait, mais j'utilise des tabulations au lieu de l'espace pour les retraits. Dans Python 3.2+, vous pouvez spécifier que le retrait soit un '\ t' directement, mais pas dans 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 '}'

Ex:

>>> 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"
    }
}
Al Conrad
la source
Je ne peux pas le faire fonctionner avec le dict imbriqué de ma solution, car cela m'a donné un UnicodeEncodeError, également les éléments et les clés sont tous convertis en chaînes, que se passe-t-il si nous utilisons des nombres ou des tuples qui contiennent des listes et des dict? Enfin, votre solution tient compte du fait que notre objet que nous voulons imprimer doit être un dicton.
y.petremann
Je n'essayais pas d'écrire une fonction d'impression générique pour un dict python. Les commentaires les mieux notés montrent déjà comment imprimer un dict. Ma contribution a été d'écrire une alternative à json.dumps avec '\ t' pour l'indentation au lieu des onglets en python 2.7.
Al Conrad
Je suis d'accord avec vous sur l'écriture d'une alternative à json.dumps, pour moi les mêmes problèmes que pour json.dumps s'appliquent. En outre, vous pouvez utiliser une expression régulière simple pour modifier le type d'indentation, ce qui rend votre code plus simple.
y.petremann
0

Voici quelque chose qui imprimera n'importe quelle sorte de dictionnaire imbriqué, tout en gardant une trace des dictionnaires "parents" en cours de route.

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)

Il s'agit d'un bon point de départ pour l'impression selon différents formats, comme celui spécifié dans OP. Tout ce que vous avez vraiment besoin de faire est d'opérer autour des blocs d' impression . Notez qu'il cherche à voir si la valeur est 'OrderedDict ()'. Selon que vous utilisez quelque chose à partir des collections de types de données Container , vous devez créer ce type de sécurité pour que le bloc elif ne le considère pas comme un dictionnaire supplémentaire en raison de son nom. Pour l'instant, un exemple de dictionnaire comme

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

imprimera

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']

~ modification du code pour l'adapter au format de la question ~

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

En utilisant le même exemple de code, il imprimera ce qui suit:

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

Ce n'est pas exactement ce qui est demandé dans OP. La différence est qu'un parent ^ n est toujours imprimé, au lieu d'être absent et remplacé par un espace blanc. Pour accéder au format OP, vous devrez faire quelque chose comme ceci: comparer de manière itérative dicList avec le lastDict . Vous pouvez le faire en créant un nouveau dictionnaire et en y copiant le contenu de dicList, en vérifiant si i dans le dictionnaire copié est le même que i dans lastDict, et - si c'est le cas - en écrivant des espaces dans cette position i en utilisant la fonction multiplicateur de chaîne .

gavin
la source
0

De ce lien :

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='<b>', keySuffix='</b>'). 
   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 '&nbsp;', and
      the entire output will be wrapped with '<code>' and '</code>'.
br: determine the carriage return. If html, it is suggested to set
    br to '<br>'. If you want the html source code eazy to read,
    set br to '<br>\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 '<code>%s</code>' %br.join(tmp).replace(' ','&nbsp;')
        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="<b>", keySuffix="</b>")
{
 <b>'C'   </b>:2,
 <b>'B'   </b>:1,
 <b>'E'   </b>:4,
 <b>(3, 5)</b>:0
}

>>> print prnDict(a, html=1)
<code>{
&nbsp;'C'&nbsp;&nbsp;&nbsp;:2,
&nbsp;'B'&nbsp;&nbsp;&nbsp;:1,
&nbsp;'E'&nbsp;&nbsp;&nbsp;:4,
&nbsp;(3,&nbsp;5):0
}</code>

>>> 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]
}
'''
user2757572
la source
0

Je reviens juste à cette question après avoir pris la réponse de qc et fait une petite mais très utile modification. Cette fonction imprime toutes les clés de l'arborescence JSON ainsi que la taille des nœuds feuilles de cette arborescence.

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

C'est vraiment bien quand vous avez de gros objets JSON et que vous voulez savoir où se trouve la viande. Exemple :

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

Cela vous dirait que la plupart des données qui vous intéressent se trouvent probablement à l'intérieur JSON_object['key1']['key2']['value2']car la longueur de cette valeur formatée en chaîne est très grande.

Ulf Aslak
la source
0

Utilisez cette fonction:

def pretty_dict(d, n=1):
    for k in d:
        print(" "*n + k)
        try:
            pretty_dict(d[k], n=n+4)
        except TypeError:
            continue

Appelez ça comme ceci:

pretty_dict(mydict)
cinquante cartes
la source
Cela ne fonctionne pas si les valeurs sont des chaînes. Il imprime chaque caractère de la chaîne sur une nouvelle ligne, mais les touches semblent fonctionner correctement.
Anthony
0

Voici ce que j'ai trouvé en travaillant sur une classe qui avait besoin d'écrire un dictionnaire dans un fichier .txt:

@staticmethod
def _pretty_write_dict(dictionary):

    def _nested(obj, level=1):
        indentation_values = "\t" * level
        indentation_braces = "\t" * (level - 1)
        if isinstance(obj, dict):
            return "{\n%(body)s%(indent_braces)s}" % {
                "body": "".join("%(indent_values)s\'%(key)s\': %(value)s,\n" % {
                    "key": str(key),
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for key, value in obj.items()),
                "indent_braces": indentation_braces
            }
        if isinstance(obj, list):
            return "[\n%(body)s\n%(indent_braces)s]" % {
                "body": "".join("%(indent_values)s%(value)s,\n" % {
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for value in obj),
                "indent_braces": indentation_braces
            }
        else:
            return "\'%(value)s\'" % {"value": str(obj)}

    dict_text = _nested(dictionary)
    return dict_text

Maintenant, si nous avons un dictionnaire comme celui-ci:

some_dict = {'default': {'ENGINE': [1, 2, 3, {'some_key': {'some_other_key': 'some_value'}}], 'NAME': 'some_db_name', 'PORT': '', 'HOST': 'localhost', 'USER': 'some_user_name', 'PASSWORD': 'some_password', 'OPTIONS': {'init_command': 'SET foreign_key_checks = 0;'}}}

Et nous faisons:

print(_pretty_write_dict(some_dict))

On a:

{
    'default': {
        'ENGINE': [
            '1',
            '2',
            '3',
            {
                'some_key': {
                    'some_other_key': 'some_value',
                },
            },
        ],
        'NAME': 'some_db_name',
        'OPTIONS': {
            'init_command': 'SET foreign_key_checks = 0;',
        },
        'HOST': 'localhost',
        'USER': 'some_user_name',
        'PASSWORD': 'some_password',
        'PORT': '',
    },
}
Edgardo Obregón
la source