Há alguma maneira de imprimir corretamente os dicionários solicitados?

97

Eu gosto do módulo pprint em Python. Eu o uso muito para teste e depuração. Eu freqüentemente uso a opção de largura para garantir que a saída se encaixe perfeitamente na janela do meu terminal.

Funcionou bem até que adicionaram o novo tipo de dicionário ordenado no Python 2.7 (outro recurso legal de que gosto muito). Se tento imprimir um dicionário ordenado de maneira bonita, ele não fica bem. Em vez de ter cada par de valores-chave em sua própria linha, a coisa toda aparece em uma longa linha, que envolve muitas vezes e é difícil de ler.

Alguém aqui tem uma maneira de fazê-lo imprimir bem, como os antigos dicionários não ordenados? Provavelmente poderia descobrir algo, possivelmente usando o método PrettyPrinter.format, se eu gastar tempo suficiente, mas estou me perguntando se alguém aqui já conhece uma solução.

ATUALIZAÇÃO: enviei um relatório de bug para isso. Você pode vê-lo em http://bugs.python.org/issue10592 .

Elias Zamaria
fonte
2
Sugira adicionar um comentário sobre o dicionário pedido em bugs.python.org/issue7434
Ned Deily

Respostas:

132

Como uma solução temporária, você pode tentar despejar no formato JSON. Você perde algumas informações de tipo, mas parece bom e mantém a ordem.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice
webwurst
fonte
7
@scottmrogowski Por que não simplesmente pprint.pprint(dict(data))?
Alfe
2
pprint.pprint(dict(data))funciona bem se você não se importar com a ordem das chaves. Pessoalmente, gostaria que o __repr__for OrderedDictproduzisse uma saída como essa, mas preservasse a ordem das chaves.
ws_e_c421
9
@Alfe se o dict tiver aninhado OrderedDicts, eles não serão exibidos de maneira
adequada
1
Também falha em números inteiros como chaves
DimmuR
2
@Alfe: Porque então a saída não é ordenada. O motivo pelo qual OrderedDict é usado em vez de dict, em primeiro lugar, é porque a ordem é importante.
Teekin
15

O seguinte funcionará se a ordem de seu OrderedDict for uma classificação alfa, uma vez que pprint classificará um dicionário antes da impressão.

pprint(dict(o.items()))
kzh
fonte
2
Como OrderedDicts são ordenados por pedido de inserção, isso provavelmente se aplica a uma pequena porcentagem de usos. Independentemente disso, converter o OD em um dictdeve evitar o problema de colocar tudo em uma linha.
martineau
8

Aqui está outra resposta que funciona substituindo e usando a pprint()função de estoque internamente. Ao contrário do meu mais adiantado que irá lidar OrderedDicté dentro de outro recipiente, como um liste também deve ser capaz de lidar com quaisquer argumentos de palavra-chave opcionais dadas - no entanto, não têm o mesmo grau de controle sobre a saída que o outro oferecidas.

Ele opera redirecionando a saída da função de estoque para um buffer temporário e, em seguida, quebra a palavra antes de enviá-la para o fluxo de saída. Embora a saída final produzida não seja excepcionalmente bonita, é decente e pode ser "boa o suficiente" para ser usada como uma solução alternativa.

Atualização 2.0

Simplificado usando o textwrapmódulo de biblioteca padrão e modificado para funcionar em Python 2 e 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)))]

Saída de amostra:

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
fonte
Eu tentei isso e funcionou. Como você disse, não é das mais bonitas, mas é a melhor solução que já vi até agora.
Elias Zamaria
7

Para imprimir um dicionário ordenado, por exemplo

from collections import OrderedDict

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

eu faço

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)

Que rende

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

ou

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

que produz

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

Esta é uma maneira que hackeia a implementação de pprint. pprintclassifica as chaves antes de imprimir, portanto, para preservar a ordem, só precisamos fazer com que as chaves sejam classificadas da maneira que desejamos.

Observe que isso afeta a items()função. Portanto, você pode querer preservar e restaurar as funções substituídas após fazer o 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'}
garupa
fonte
2
Bom, mas melhor subtipo do que substituir funções.
xmedeko
3

Aqui está minha abordagem para imprimir um 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 você quiser imprimir o dicionário com as chaves em ordem classificada

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
CHINTAN VADGAMA
fonte
@AlxVallejo Você pode estar usando python3. Verifique
CHINTAN VADGAMA
2

Isso é muito rudimentar, mas eu só precisava de uma maneira de visualizar uma estrutura de dados composta de quaisquer Mapeamentos e Iterables arbitrários e foi isso que pensei antes de desistir. É recursivo, por isso vai passar por estruturas e listas aninhadas muito bem. Usei as classes de base abstratas Mapping e Iterable de coleções para lidar com quase tudo.

Eu queria uma saída quase yaml com código python conciso, mas não consegui.

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 alguns dados de teste usando OrderedDict e listas de OrderedDicts ... (sheesh Python precisa de literais OrderedDict muuuito 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)

produz a seguinte saída:

   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

Pensei em usar str.format () para um melhor alinhamento, mas não tive vontade de aprofundar. Você precisaria especificar dinamicamente as larguras do campo, dependendo do tipo de alinhamento que deseja, o que seria complicado ou complicado.

De qualquer forma, isso me mostra meus dados de forma hierárquica legível, então isso funciona para mim!

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

Lá vai você ^^

for item in li:
    pprint_od(item)

ou

(pprint_od(item) for item in li)
Jakob Bowyer
fonte
Estou procurando uma maneira de ter uma função que possa imprimir OrderedDicts bem como outros tipos. Não vejo como eu usaria sua função para imprimir, digamos, uma lista de OrderedDicts.
Elias Zamaria
-1 A pprint_od()função não funciona - a for key, item in odinstrução resulta em a ValueError: too many values to unpack e a única saída indentada é a final " }" e a key, itemna printinstrução precisa estar entre parênteses. Aí está ^^
martineau
2

Eu testei esse hack profano baseado em patch de macaco no python3.5 e ele 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

Você pode pprintusar o resumo baseado em dict usual e também desabilitar a classificação durante a chamada para que nenhuma chave seja realmente classificada para impressão.

Karl Rosaen
fonte
você também pode copiar o pretty_print.pycomo um módulo local e hackear (removendo a sortedchamada ou o que quiser).
Karl Rosaen
2

A partir do Python 3.8: pprint.PrettyPrinterexpõe o sort_dictsparâmetro de palavra - chave.

Verdadeiro por padrão, defini-lo como Falso deixará o dicionário sem classificação.

>>> from pprint import PrettyPrinter

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

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

Irá produzir:

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

Referência: https://docs.python.org/3/library/pprint.html

mxdbld
fonte
1

O pprint()método é apenas invocar o __repr__()método das coisas nele, eOrderedDict não parece fazer muito em seu método (ou não tem um ou algo assim).

Aqui está uma solução barata que deve funcionar SE VOCÊ NÃO SE PREOCUPAR COM O PEDIDO ESTÁ VISÍVEL NA SAÍDA DE IMPRESSÃO , que pode ser grande se:

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

Estou realmente surpreso que a ordem não seja preservada ... ah, bem.

Bill M.
fonte
Um dicionário python é implementado usando um hashmap. Portanto, depois de converter um OrderedDict (combinação de um dicionário básico e uma lista para preservar o pedido) em um dicionário, você perde todas as informações do pedido. Além disso, o método repr deve retornar uma string que representaria o objeto no código python. Em outras palavras, obj == eval (repr (obj)), ou, no mínimo repr (obj) == repr (eval (repr (obj))). A reprovação de OrderedDict faz isso muito bem. dict .__ repr__ dar a você uma representação legível muito humana é completamente um efeito colateral do literal dict ('{' e '}', etc.). OrderedDict não tem isso.
março de 75
1

Você também pode usar esta simplificação da resposta kzh :

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

Ele preserva a ordem e produzirá quase o mesmo que a resposta do webwurst ( imprimir por meio do json dump ).

Albert Alomar
fonte
1

Para python <3.8 (por exemplo, 3.6):

Remendo macaco pprint's sorted, a fim de impedi-lo de triagem. Isso terá o benefício de tudo funcionar recursivamente também, e é mais adequado do que a jsonopção para quem precisa usar, por exemplo, o 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: limpeza

Para limpar esse negócio sujo, basta executar: pprint.sorted = sorted

Para uma solução realmente limpa pode até usar um gerenciador 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
fonte
0

Você pode redefinir pprint()e interceptar chamadas para OrderedDict's. Aqui está uma ilustração simples. Como escrito, o OrderedDictcódigo override ignora quaisquer opcionais stream, indent, width, ou depthpalavras-chave que podem ter sido passados, mas poderia ser melhorado para implementá-las. Infelizmente, essa técnica não os manipula dentro de outro contêiner, como um 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
fonte
0

Se os itens do dicionário forem todos de um tipo, você pode usar a incrível biblioteca de manipulação de dados pandas:

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

ou

>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz    bam
foo    bar
dtype: object
LondonRob
fonte
2
O que esse código fará? Que vantagens isso tem sobre as outras soluções aqui?
Elias Zamaria