Qualche modo per stampare correttamente i dizionari ordinati?


97

Mi piace il modulo pprint in Python. Lo uso molto per il test e il debug. Uso spesso l'opzione width per assicurarmi che l'output si adatti perfettamente alla finestra del mio terminale.

Ha funzionato bene fino a quando non hanno aggiunto il nuovo tipo di dizionario ordinato in Python 2.7 (un'altra caratteristica interessante che mi piace molto). Se provo a stampare in modo carino un dizionario ordinato, non viene visualizzato bene. Invece di avere ogni coppia chiave-valore su una propria riga, l'intera cosa viene visualizzata su una lunga riga, che si avvolge molte volte ed è difficile da leggere.

Qualcuno qui ha un modo per farlo stampare bene, come i vecchi dizionari non ordinati? Probabilmente potrei capire qualcosa, possibilmente usando il metodo PrettyPrinter.format, se passo abbastanza tempo, ma mi chiedo se qualcuno qui conosce già una soluzione.

AGGIORNAMENTO: ho presentato una segnalazione di bug per questo. Puoi vederlo su http://bugs.python.org/issue10592 .


2
Suggerisci di aggiungere un commento sul dizionario ordinato a bugs.python.org/issue7434
Ned Deily

Risposte:


132

Come soluzione temporanea, puoi provare a eseguire il dumping in formato JSON. Perdi alcune informazioni sul tipo, ma sembra carino e mantiene l'ordine.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice

7
@scottmrogowski Perché non semplicemente pprint.pprint(dict(data))?
Alfe

2
pprint.pprint(dict(data))funziona bene se non ti interessa l'ordine delle chiavi. Personalmente, vorrei che __repr__for OrderedDictproducesse un output come questo ma preservasse l'ordine dei tasti.
ws_e_c421

9
@Alfe se il dict ha OrderedDict nidificati non verranno visualizzati
correttamente

1
Fallisce anche sugli interi come chiavi
DimmuR

2
@Alfe: Perché allora l'output non è ordinato. Il motivo per cui OrderedDict viene utilizzato al posto di dict in primo luogo è perché l'ordine è importante.
Teekin

15

Quanto segue funzionerà se l'ordine del tuo OrderedDict è un ordinamento alfa, poiché pprint ordinerà un dict prima della stampa.

pprint(dict(o.items()))

2
Poiché OrderedDicts sono ordinati per ordine di inserzione, quindi questo probabilmente si applica a una piccola percentuale di utilizzi. Indipendentemente da ciò, la conversione dell'OD in a dictdovrebbe evitare il problema di posizionare tutto su una riga.
martineau

8

Ecco un'altra risposta che funziona sovrascrivendo e utilizzando pprint()internamente la funzione stock . A differenza di mio in meno un esso sarà gestire OrderedDict's all'interno di un altro contenitore come una liste dovrebbe anche essere in grado di gestire eventuali argomenti a parola chiave opzionali forniti - tuttavia non ha lo stesso grado di controllo sul risultato che l'altro offriva.

Funziona reindirizzando l'output della funzione stock in un buffer temporaneo e quindi lo avvolge prima di inviarlo al flusso di output. Sebbene l'output finale prodotto non sia eccezionalmente bello, è decente e potrebbe essere "abbastanza buono" da essere utilizzato come soluzione alternativa.

Aggiorna 2.0

Semplificato utilizzando il textwrapmodulo libreria standard e modificato per funzionare sia in Python 2 che in 3.

from collections import OrderedDict
try:
    from cStringIO import StringIO
except ImportError:  # Python 3
    from io import StringIO
from pprint import pprint as pp_pprint
import sys
import textwrap

def pprint(object, **kwrds):
    try:
        width = kwrds['width']
    except KeyError: # unlimited, use stock function
        pp_pprint(object, **kwrds)
        return
    buffer = StringIO()
    stream = kwrds.get('stream', sys.stdout)
    kwrds.update({'stream': buffer})
    pp_pprint(object, **kwrds)
    words = buffer.getvalue().split()
    buffer.close()

    # word wrap output onto multiple lines <= width characters
    try:
        print >> stream, textwrap.fill(' '.join(words), width=width)
    except TypeError:  # Python 3
        print(textwrap.fill(' '.join(words), width=width), file=stream)

d = dict((('john',1), ('paul',2), ('mary',3)))
od = OrderedDict((('john',1), ('paul',2), ('mary',3)))
lod = [OrderedDict((('john',1), ('paul',2), ('mary',3))),
       OrderedDict((('moe',1), ('curly',2), ('larry',3))),
       OrderedDict((('weapons',1), ('mass',2), ('destruction',3)))]

Output di esempio:

pprint(d, width=40)

»   {'john': 1, 'mary': 3, 'paul': 2}

pprint(od, width=40)

» OrderedDict([('john', 1), ('paul', 2),
   ('mary', 3)])

pprint(lod, width=40)

» [OrderedDict([('john', 1), ('paul', 2),
   ('mary', 3)]), OrderedDict([('moe', 1),
   ('curly', 2), ('larry', 3)]),
   OrderedDict([('weapons', 1), ('mass',
   2), ('destruction', 3)])]


L'ho provato e funziona. Come hai detto, non è la più bella, ma è la migliore soluzione che ho visto finora.
Elias Zamaria

7

Per stampare un dict ordinato, ad es

from collections import OrderedDict

d=OrderedDict([
    ('a', OrderedDict([
        ('a1',1),
        ('a2','sss')
    ])),
    ('b', OrderedDict([
        ('b1', OrderedDict([
            ('bb1',1),
            ('bb2',4.5)])),
        ('b2',4.5)
    ])),
])

lo voglio

def dict_or_OrdDict_to_formatted_str(OD, mode='dict', s="", indent=' '*4, level=0):
    def is_number(s):
        try:
            float(s)
            return True
        except ValueError:
            return False
    def fstr(s):
        return s if is_number(s) else '"%s"'%s
    if mode != 'dict':
        kv_tpl = '("%s", %s)'
        ST = 'OrderedDict([\n'; END = '])'
    else:
        kv_tpl = '"%s": %s'
        ST = '{\n'; END = '}'
    for i,k in enumerate(OD.keys()):
        if type(OD[k]) in [dict, OrderedDict]:
            level += 1
            s += (level-1)*indent+kv_tpl%(k,ST+dict_or_OrdDict_to_formatted_str(OD[k], mode=mode, indent=indent, level=level)+(level-1)*indent+END)
            level -= 1
        else:
            s += level*indent+kv_tpl%(k,fstr(OD[k]))
        if i!=len(OD)-1:
            s += ","
        s += "\n"
    return s

print dict_or_OrdDict_to_formatted_str(d)

Che produce

"a": {
    "a1": 1,
    "a2": "sss"
},
"b": {
    "b1": {
        "bb1": 1,
        "bb2": 4.5
    },
    "b2": 4.5
}

o

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

che produce

("a", OrderedDict([
    ("a1", 1),
    ("a2", "sss")
])),
("b", OrderedDict([
    ("b1", OrderedDict([
        ("bb1", 1),
        ("bb2", 4.5)
    ])),
    ("b2", 4.5)
]))

5

Ecco un modo per hackerare l'implementazione di pprint. pprintordina le chiavi prima della stampa, quindi per preservare l'ordine, dobbiamo solo fare in modo che le chiavi si ordinino nel modo desiderato.

Nota che questo influisce sulla items()funzione. Quindi potresti voler preservare e ripristinare le funzioni sovrascritte dopo aver eseguito pprint.

from collections import OrderedDict
import pprint

class ItemKey(object):
  def __init__(self, name, position):
    self.name = name
    self.position = position
  def __cmp__(self, b):
    assert isinstance(b, ItemKey)
    return cmp(self.position, b.position)
  def __repr__(self):
    return repr(self.name)

OrderedDict.items = lambda self: [
    (ItemKey(name, i), value)
    for i, (name, value) in enumerate(self.iteritems())]
OrderedDict.__repr__ = dict.__repr__

a = OrderedDict()
a[4] = '4'
a[1] = '1'
a[2] = '2'
print pprint.pformat(a) # {4: '4', 1: '1', 2: '2'}

2
Bello, ma è meglio sottotitolare quindi sovrascrivere le funzioni.
xmedeko

3

Ecco il mio approccio per stampare piuttosto un OrderedDict

from collections import OrderedDict
import json
d = OrderedDict()
d['duck'] = 'alive'
d['parrot'] = 'dead'
d['penguin'] = 'exploded'
d['Falcon'] = 'discharged'
print(d)
print(json.dumps(d,indent=4))

OutPut:

OrderedDict([('duck', 'alive'), ('parrot', 'dead'), ('penguin', 'exploded'), ('Falcon', 'discharged')])

{
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded",
    "Falcon": "discharged"
}

Se vuoi stampare piuttosto il dizionario con i tasti in ordine

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}

@AlxVallejo Potresti usare python3. Si prega di controllare
CHINTAN VADGAMA

2

Questo è piuttosto rozzo, ma avevo solo bisogno di un modo per visualizzare una struttura dati composta da qualsiasi mappatura e iterabile arbitrari e questo è ciò che mi è venuto in mente prima di arrendermi. È ricorsivo, quindi ricadrà su strutture ed elenchi annidati. Ho usato le classi base astratte Mapping e Iterable dalle raccolte per gestire praticamente qualsiasi cosa.

Stavo mirando a un output quasi simile a yaml con codice python conciso, ma non ce l'ho fatta.

def format_structure(d, level=0):
    x = ""
    if isinstance(d, Mapping):
        lenk = max(map(lambda x: len(str(x)), d.keys()))
        for k, v in d.items():
            key_text = "\n" + " "*level + " "*(lenk - len(str(k))) + str(k)
            x += key_text + ": " + format_structure(v, level=level+lenk)
    elif isinstance(d, Iterable) and not isinstance(d, basestring):
        for e in d:
            x += "\n" + " "*level + "- " + format_structure(e, level=level+4)
    else:
        x = str(d)
    return x

e alcuni dati di test usando OrderedDict e elenchi di OrderedDicts ... (Sheesh Python ha bisogno di letterali OrderedDict così male ...)

d = OrderedDict([("main",
                  OrderedDict([("window",
                                OrderedDict([("size", [500, 500]),
                                             ("position", [100, 900])])),
                               ("splash_enabled", True),
                               ("theme", "Dark")])),
                 ("updates",
                  OrderedDict([("automatic", True),
                               ("servers",
                                [OrderedDict([("url", "http://server1.com"),
                                              ("name", "Stable")]),
                                 OrderedDict([("url", "http://server2.com"),
                                              ("name", "Beta")]),
                                 OrderedDict([("url", "http://server3.com"),
                                              ("name", "Dev")])]),
                               ("prompt_restart", True)])),
                 ("logging",
                  OrderedDict([("enabled", True),
                               ("rotate", True)]))])

print format_structure(d)

produce il seguente output:

   main: 
               window: 
                         size: 
                             - 500
                             - 500
                     position: 
                             - 100
                             - 900
       splash_enabled: True
                theme: Dark
updates: 
            automatic: True
              servers: 
                     - 
                          url: http://server1.com
                         name: Stable
                     - 
                          url: http://server2.com
                         name: Beta
                     - 
                          url: http://server3.com
                         name: Dev
       prompt_restart: True
logging: 
       enabled: True
        rotate: True

Ho avuto alcuni pensieri lungo il modo di utilizzare str.format () per un migliore allineamento, ma non avevo voglia di scavare in esso. Dovresti specificare dinamicamente le larghezze del campo a seconda del tipo di allineamento che desideri, il che potrebbe diventare complicato o macchinoso.

Ad ogni modo, questo mi mostra i miei dati in modo gerarchico leggibile, quindi per me funziona!


2
def pprint_od(od):
    print "{"
    for key in od:
        print "%s:%s,\n" % (key, od[key]) # Fixed syntax
    print "}"

Ecco ^^

for item in li:
    pprint_od(item)

o

(pprint_od(item) for item in li)

Sto cercando un modo per avere una funzione che possa stampare in modo carino OrderedDicts e altri tipi. Non vedo come potrei usare la tua funzione per stampare abbastanza, ad esempio, un elenco di OrderedDicts.
Elias Zamaria

-1 La pprint_od()funzione non è - i for key, item in odrisultati dichiarazione in una ValueError: too many values to unpack e l'unica uscita frastagliata è la finale " }" e la key, itemnella printnecessità dichiarazione di essere in parentesi. Ecco ^^
martineau

2

Ho testato questo scellerato hack basato su patch di scimmia su python3.5 e funziona:

pprint.PrettyPrinter._dispatch[pprint._collections.OrderedDict.__repr__] = pprint.PrettyPrinter._pprint_dict


def unsorted_pprint(data):
    def fake_sort(*args, **kwargs):
        return args[0]
    orig_sorted = __builtins__.sorted
    try:
        __builtins__.sorted = fake_sort
        pprint.pprint(data)
    finally:
        __builtins__.sorted = orig_sorted

Si pprintutilizza il solito riepilogo basato su dict e si disabilita anche l'ordinamento per la durata della chiamata in modo che nessuna chiave venga effettivamente ordinata per la stampa.


puoi anche semplicemente copiare il pretty_print.pycome modulo locale e hackerarlo (rimuovendo la sortedchiamata o qualsiasi altra cosa tu voglia).
Karl Rosaen

2

A partire da Python 3.8: pprint.PrettyPrinterespone il sort_dictsparametro della parola chiave.

True per impostazione predefinita, impostandolo su False il dizionario non verrà ordinato.

>>> from pprint import PrettyPrinter

>>> x = {'John': 1,
>>>      'Mary': 2,
>>>      'Paul': 3,
>>>      'Lisa': 4,
>>>      }

>>> PrettyPrinter(sort_dicts=False).pprint(x)

Produrrà:

{'John': 1, 
 'Mary': 2, 
 'Paul': 3,
 'Lisa': 4}

Riferimento: https://docs.python.org/3/library/pprint.html


1

Il pprint()metodo sta solo invocando il __repr__()metodo delle cose in esso e OrderedDictnon sembra fare molto nel suo metodo (o non ne ha uno o qualcosa del genere).

Ecco una soluzione economica che dovrebbe funzionare SE NON TI CURA CHE L'ORDINE SIA VISIBILE NELL'USCITA DI STAMPA , il che potrebbe essere grande se:

class PrintableOrderedDict(OrderedDict):
    def __repr__(self):
        return dict.__repr__(self)

In realtà sono sorpreso che l'ordine non sia stato mantenuto ... ah beh.


Un dizionario Python viene implementato utilizzando una hashmap. Pertanto, una volta convertito un OrderedDict (combinazione di un dict di base e un elenco per preservare l'ordine) in un dict, si perdono le informazioni sull'ordine. Inoltre, il metodo repr dovrebbe restituire una stringa che rappresenterebbe l'oggetto nel codice Python. In altre parole, obj == eval (repr (obj)), o, al minimo repr (obj) == repr (eval (repr (obj))). La riproduzione di OrderedDict lo fa benissimo. dict .__ repr__ che fornisce una rappresentazione leggibile dall'uomo è completamente un effetto collaterale del letterale dict ('{' e '}', ecc.). OrderedDict non ha questo.
marr75

1

Puoi anche usare questa semplificazione della risposta kzh :

pprint(data.items(), indent=4)

Conserva l'ordine e restituirà quasi lo stesso della risposta del webwurst ( stampa tramite json dump ).


1

Per python <3.8 (ad esempio 3.6):

Toppa Scimmia pprint's sortedal fine di evitare che l'ordinamento. Questo avrà il vantaggio di tutto ciò che funziona anche in modo ricorsivo, ed è più adatto jsondell'opzione per chi ha bisogno di usare ad esempio il widthparametro:

import pprint
pprint.sorted = lambda arg, *a, **kw: arg

>>> pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)
{'z': 1,
 'a': 2,
 'c': {'z': 0,
       'a': 1}}

Modifica: pulizia

Per ripulire dopo questo sporco affare, corri: pprint.sorted = sorted

Per una soluzione davvero pulita puoi anche utilizzare un contextmanager:

import pprint
import contextlib

@contextlib.contextmanager
def pprint_ordered():
    pprint.sorted = lambda arg, *args, **kwargs: arg
    yield
    pprint.sorted = sorted

# usage:

with pprint_ordered():
    pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)

# without it    
pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)

# prints: 
#    
# {'z': 1,
#  'a': 2,
#  'c': {'z': 0,
#        'a': 1}}
#
# {'a': 2,
#  'c': {'a': 1,
#        'z': 0},
#  'z': 1}

0

Potresti ridefinire pprint()e intercettare le chiamate per OrderedDict. Ecco una semplice illustrazione. Come scritto, il OrderedDictcodice override ignora eventuali opzionali stream, indent, widtho depthle parole chiave che possono essere stati passati, ma potrebbe essere migliorata per la loro attuazione. Purtroppo questa tecnica non li maniglia all'interno di un altro contenitore, come una listdi OrderDicts'

from collections import OrderedDict
from pprint import pprint as pp_pprint

def pprint(obj, *args, **kwrds):
    if not isinstance(obj, OrderedDict):
        # use stock function
        return pp_pprint(obj, *args, **kwrds)
    else:
        # very simple sample custom implementation...
        print "{"
        for key in obj:
            print "    %r:%r" % (key, obj[key])
        print "}"

l = [10, 2, 4]
d = dict((('john',1), ('paul',2), ('mary',3)))
od = OrderedDict((('john',1), ('paul',2), ('mary',3)))
pprint(l, width=4)
# [10,
#  2,
#  4]
pprint(d)
# {'john': 1, 'mary': 3, 'paul': 2}

pprint(od)
# {
#     'john':1
#     'paul':2
#     'mary':3
# }

0

Se gli elementi del dizionario sono tutti di un tipo, puoi utilizzare la straordinaria libreria di gestione dei dati pandas:

>>> import pandas as pd
>>> x = {'foo':1, 'bar':2}
>>> pd.Series(x)
bar    2
foo    1
dtype: int64

o

>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz    bam
foo    bar
dtype: object

2
Cosa farà quel codice? Quali vantaggi ha rispetto alle altre soluzioni qui?
Elias Zamaria
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.