¿Alguna forma de imprimir correctamente los diccionarios ordenados?

97

Me gusta el módulo pprint en Python. Lo uso mucho para probar y depurar. Con frecuencia uso la opción de ancho para asegurarme de que la salida se ajuste bien dentro de la ventana de mi terminal.

Ha funcionado bien hasta que agregaron el nuevo tipo de diccionario ordenado en Python 2.7 (otra característica interesante que realmente me gusta). Si trato de imprimir con estilo un diccionario ordenado, no se ve bien. En lugar de tener cada par clave-valor en su propia línea, todo aparece en una línea larga, que se ajusta muchas veces y es difícil de leer.

¿Alguien aquí tiene una forma de hacer que se imprima bien, como los viejos diccionarios desordenados? Probablemente podría resolver algo, posiblemente usando el método PrettyPrinter.format, si dedico suficiente tiempo, pero me pregunto si alguien aquí ya conoce una solución.

ACTUALIZACIÓN: Presenté un informe de error para esto. Puede verlo en http://bugs.python.org/issue10592 .

Elias Zamaria
fuente
2
Sugerir agregar un comentario sobre el diccionario ordenado a bugs.python.org/issue7434
Ned Deily

Respuestas:

132

Como solución temporal, puede intentar realizar un volcado en formato JSON. Pierde algún tipo de información, pero se ve bien y mantiene el orden.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice
webwurst
fuente
7
@scottmrogowski ¿Por qué no simplemente pprint.pprint(dict(data))?
Alfe
2
pprint.pprint(dict(data))funciona bien si no le importa el orden de las teclas. Personalmente, desearía que el __repr__for OrderedDictprodujera un resultado como este pero conservara el orden de las claves.
ws_e_c421
9
@Alfe si el dictado ha anidado OrderedDicts, no se mostrarán bien
Catskul
1
También falla en números enteros como claves
DimmuR
2
@Alfe: Porque entonces la salida no está ordenada. La razón por la que se usa OrderedDict en lugar de dict en primer lugar, es porque el orden importa.
Teekin
15

Lo siguiente funcionará si el orden de su OrderedDict es alfa, ya que pprint clasificará un dictado antes de imprimirlo.

pprint(dict(o.items()))
kzh
fuente
2
Dado que los OrderedDicts se ordenan por orden de inserción, esto probablemente se aplique a un pequeño porcentaje de usos. Independientemente, convertir el OD en a dictdebería evitar el problema de que todo se coloque en una línea.
martineau
8

Aquí hay otra respuesta que funciona anulando y usando la pprint()función de stock internamente. A diferencia del anterior , se manejará OrderedDictdentro de otro contenedor como a listy también debería poder manejar cualquier argumento de palabra clave opcional dado; sin embargo, no tiene el mismo grado de control sobre la salida que el otro.

Opera redirigiendo la salida de la función de stock a un búfer temporal y luego envuelve la palabra antes de enviarla al flujo de salida. Si bien el resultado final producido no es excepcionalmente bonito, es decente y puede ser "lo suficientemente bueno" para usarlo como solución.

Actualización 2.0

Simplificado mediante el uso de un textwrapmódulo de biblioteca estándar y modificado para funcionar en Python 2 y 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)))]

Salida de muestra:

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

martineau
fuente
Intenté eso y funciona. Como dijiste, no es la más bonita, pero es la mejor solución que he visto hasta ahora.
Elias Zamaria
7

Para imprimir un dictado ordenado, p. Ej.

from collections import OrderedDict

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

hago

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)

Cuyos rendimientos

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

cuyos rendimientos

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

Aquí hay una forma que piratea la implementación de pprint. pprintordena las claves antes de imprimir, por lo que para preservar el orden, solo tenemos que hacer que las claves se ordenen de la manera que queramos.

Tenga en cuenta que esto afecta la items()función. Por lo tanto, es posible que desee conservar y restaurar las funciones anuladas después de realizar el 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'}
rumpel
fuente
2
Agradable, pero mejor subtipificar y anular funciones.
xmedeko
3

Aquí está mi enfoque para imprimir 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"
}

Si desea imprimir un diccionario bonito con claves en orden ordenado

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
CHINTAN VADGAMA
fuente
@AlxVallejo Puede que estés usando python3. Por favor verifique
CHINTAN VADGAMA
2

Esto es bastante burdo, pero solo necesitaba una forma de visualizar una estructura de datos formada por cualquier Mapeo e iteración arbitrarios y esto es lo que se me ocurrió antes de rendirme. Es recursivo, por lo que caerá bien a través de estructuras y listas anidadas. Usé las clases base abstractas Mapping e Iterable de colecciones para manejar casi cualquier cosa.

Apuntaba a una salida casi similar a yaml con un código de Python conciso, pero no lo logré.

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

y algunos datos de prueba usando OrderedDict y listas de OrderedDicts ... (sheesh Python necesita los literales OrderedDict tan mal ...)

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 el siguiente resultado:

   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

Tuve algunas ideas sobre el uso de str.format () para una mejor alineación, pero no tenía ganas de profundizar en ello. Debería especificar dinámicamente los anchos de campo según el tipo de alineación que desee, lo que se volvería complicado o engorroso.

De todos modos, esto me muestra mis datos de una manera jerárquica legible, ¡así que eso funciona para mí!

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

Ahí tienes ^^

for item in li:
    pprint_od(item)

o

(pprint_od(item) for item in li)
Jakob Bowyer
fuente
Estoy buscando alguna forma de tener una función que pueda imprimir OrderedDicts y otros tipos. No veo cómo usaría su función para imprimir bastante, digamos, una lista de OrderedDicts.
Elias Zamaria
-1 La pprint_od()función no está disponible - los for key, item in odresultados de la instrucción en una ValueError: too many values to unpack y la única salida de sangría es la final " }" y el key, itemde la printnecesidad de ser comunicado entre paréntesis. Ahí tienes ^^
martineau
2

He probado este truco profano basado en parche de mono en python3.5 y funciona:

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

Haces que pprintutilice el resumen basado dict habitual y clasificación también desactivar durante la duración de la llamada de modo que no hay ninguna tecla en realidad clasifican para la impresión.

Karl Rosaen
fuente
también puede simplemente copiar el pretty_print.pycomo un módulo local y piratearlo (eliminar la sortedllamada o lo que desee).
Karl Rosaen
2

A partir de Python 3.8: pprint.PrettyPrinterexpone el sort_dictsparámetro de palabra clave.

Verdadero de forma predeterminada, establecerlo en Falso dejará el diccionario sin clasificar.

>>> from pprint import PrettyPrinter

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

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

Saldrá:

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

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

mxdbld
fuente
1

El pprint()método simplemente invoca el __repr__()método de las cosas en él, y OrderedDictno parece hacer mucho en su método (o no tiene uno o algo).

Aquí hay una solución económica que debería funcionar SI NO LE IMPORTA QUE EL PEDIDO SEA VISIBLE EN LA SALIDA DE IMPRESIÓN , lo que puede ser un gran si:

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

De hecho, me sorprende que no se conserve el orden ... bueno.

Bill M.
fuente
Un diccionario de Python se implementa mediante un mapa de hash. Por lo tanto, una vez que convierte un OrderedDict (combinación de un dict básico y una lista para preservar el orden) en un dict, pierde toda la información del pedido. Además, se supone que el método repr devuelve una cadena que representaría el objeto en código Python. En otras palabras, obj == eval (repr (obj)), o, como mínimo, repr (obj) == repr (eval (repr (obj))). La repr. De OrderedDict hace esto bien. dict .__ repr__ darle una representación legible muy humana es completamente un efecto secundario del literal dict ('{' y '}', etc.). OrderedDict no tiene esto.
marr75
1

También puede usar esta simplificación de la respuesta kzh :

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

Conserva el orden y generará casi lo mismo que la respuesta webwurst ( imprimir a través de json dump ).

Albert Alomar
fuente
1

Para python <3.8 (por ejemplo, 3.6):

Parche mono pprint's sortedcon el fin de evitar que la clasificación. Esto tendrá el beneficio de que todo funcione de forma recursiva también, y es más adecuado que la jsonopción para quien necesite usar, por ejemplo, el widthparámetro:

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

Editar: limpieza

Para limpiar después de este sucio negocio, simplemente ejecute: pprint.sorted = sorted

Para una solución realmente limpia, incluso puede usar un administrador de contexto:

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}
artd
fuente
0

Podría redefinir pprint()e interceptar las llamadas de OrderedDict. He aquí una ilustración sencilla. Como está escrito, el OrderedDictcódigo de anulación ignora cualquier opcionales stream, indent, width, o depthpalabras clave que pueden haber sido pasadas, pero podría mejorarse para ponerlas en práctica. Por desgracia, esta técnica no se ocupa de ellos dentro de otro recipiente, tal como una listde OrderDict's

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
# }
martineau
fuente
0

Si los elementos del diccionario son todos de un tipo, puede usar la increíble biblioteca de manejo de datos 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
LondresRob
fuente
2
¿Qué hará ese código? ¿Qué ventajas tiene sobre las otras soluciones aquí?
Elias Zamaria