python - resueltos - ¿Hay alguna manera de imprimir correctamente los diccionarios ordenados?
que es un diccionario en python (11)
Aquí hay otra respuesta que funciona anulando y utilizando internamente la función stock pprint()
. A diferencia de mi anterior , manejará OrderedDict
dentro de otro contenedor, como una list
y también podrá manejar cualquier argumento de palabra clave opcional, sin embargo, no tiene el mismo grado de control sobre el resultado que el otro.
Funciona redireccionando la salida de la función stock en un búfer temporal y luego ajusta la palabra antes de enviarla a la secuencia de salida. Si bien el resultado final producido no es excepcionalmente bonito, es decente y puede ser "lo suficientemente bueno" como para utilizarlo como solución alternativa.
Actualización 2.0
Simplificado mediante el uso del módulo de textwrap
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)))]
Muestra de salida:
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)])]
Me gusta el módulo de pprint en Python. Lo uso mucho para probar y depurar. Frecuentemente utilizo la opción de ancho para asegurarme de que la salida encaja bien dentro de la ventana de mi terminal.
Funcionó bien hasta que agregaron el nuevo tipo de diccionario ordenado en Python 2.7 (otra característica genial que realmente me gusta). Si trato de imprimir bastante un diccionario ordenado, no se muestra bien. En lugar de tener cada pareja clave-valor en su propia línea, todo se muestra en una línea larga, que se envuelve muchas veces y es difícil de leer.
¿Alguien aquí tiene una forma de hacer que se imprima bien, como los viejos diccionarios sin orden? Probablemente podría resolver algo, posiblemente usando el método PrettyPrinter.format, si paso el tiempo suficiente, pero me pregunto si alguien aquí ya conoce una solución.
ACTUALIZACIÓN: Archivé un informe de error para esto. Puedes verlo en http://bugs.python.org/issue10592 .
Aquí hay una forma de pprint
la implementación de pprint
. pprint
ordena las claves antes de imprimir, por lo que para pprint
el orden, solo tenemos que hacer que las claves se clasifiquen de la manera que deseamos.
Tenga en cuenta que esto afecta la función items()
. Por lo tanto, es posible que desee conservar y restaurar las funciones anuladas después de hacer la impresión.
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''}
Como una solución temporal, puede intentar descargar en formato JSON. Pierdes 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
El método pprint()
simplemente invoca el __repr__()
de cosas en él, y OrderedDict
no parece hacer mucho en su método (o no tiene uno o algo así).
Aquí hay una solución económica que debería funcionar SI NO LE PREVE QUE EL ORDEN SEA VISIBLE EN LA PUNTUACIÓN , lo que puede ser grande si:
class PrintableOrderedDict(OrderedDict):
def __repr__(self):
return dict.__repr__(self)
De hecho, estoy sorprendido de que el pedido no se haya conservado ... ah bueno.
Esto es bastante crudo, pero solo necesitaba una forma de visualizar una estructura de datos formada por Mapping e Iterables arbitrarios, y esto es lo que se me ocurrió antes de darme por vencido. Es recursivo, por lo que caerá a través de estructuras anidadas y listas muy bien. Utilicé las clases base abstractas Mapeo e Iterable de las colecciones para manejar casi cualquier cosa.
Apuntaba casi a la salida de yaml con un código python conciso, pero no lo conseguí.
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 literales OrderedDict muuuy 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 algunos pensamientos a lo largo del camino de usar str.format () para una mejor alineación, pero no tenía ganas de profundizar en ello. Tendrá que especificar dinámicamente los anchos de campo según el tipo de alineación que desee, lo que podría ser complicado o engorroso.
De todos modos, esto me muestra mis datos en forma jerárquica legible, ¡así que eso funciona para mí!
Lo siguiente funcionará si el orden de su OrderedDict es alfa, ya que pprint ordenará un dict antes de imprimir.
pprint(dict(o.items()))
Para imprimir un dict ordenado, por ejemplo
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)
]))
Puede redefinir pprint()
e interceptar llamadas para OrderedDict
''s. Aquí hay una ilustración simple. Tal como está escrito, el código de anulación de OrderedDict
ignora las palabras clave de stream
, indent
, width
o depth
opcionales que se hayan podido pasar, pero podrían mejorarse para implementarlas. Lamentablemente, esta técnica no los maneja dentro de otro contenedor, como una list
de 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
# }
Si los ítems del diccionario son todos de un tipo, podría usar los asombrosos pandas
biblioteca de manejo de datos:
>>> 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
def pprint_od(od):
print "{"
for key in od:
print "%s:%s,/n" % (key, od[key]) # Fixed syntax
print "}"
Ahí vas ^^
for item in li:
pprint_od(item)
o
(pprint_od(item) for item in li)