pprint - Stampa Pretty-print di Strutture Dati

Scopo: pprint produce stampe esteticamente piacevoli di viste di strutture dati

Per pretty-print si intende l'applicazione di diverse convenzioni di formattazione per testo, codice sorgente, linguaggi di marcatura ecc.. Queste convenzioni in genere consistono nel cambiare posizione, spaziatura, colore, contrasto, dimensione per rendere il contenuto più facilmente leggibile e comprensibile dalle persone. (n.d.t.)

Il modulo pprint comprende un "pretty printer" per produrre rappresentazioni esteticamente piacevoli di strutture di dati. Il formattatore produce rappresentazioni di strutture di dati che possono essere correttamente elaborate dall'interprete, e che risultano facilmente leggibili all'occhio umano. L'output viene mantenuto se possibile in una singola riga e viene indentato se deve essere diviso in diverse righe.

Gli esempi qui sono tutti basati su pprint_data.py, che contiene.

# pprint_data.py

data = [
    (1, {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D'}),
    (2, {'e': 'E', 'f': 'F', 'g': 'G', 'h': 'H',
         'i': 'I', 'j': 'J', 'k': 'K', 'l': 'L'}),
]

Stampare

Il modo più semplice di usare il modulo è tramite la funzione pprint().

# pprint_pprint.py

from pprint import pprint

from pprint_data import data

print('PRINT:')
print(data)
print()
print('PPRINT:')
pprint(data)

pprint() formatta un oggetto o lo scrive nel flusso dati passato come argomento (oppure su sys.stdout nella modalità predefinita).

PRINT:
[(1, {'d': 'D', 'a': 'A', 'b': 'B', 'c': 'C'}), (2, {'l': 'L', 'j': 'J', 'i': 'I', 'h': 'H', 'f': 'F', 'k': 'K', 'e': 'E', 'g': 'G'})]

PPRINT:
[(1, {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D'}),
 (2,
  {'e': 'E',
   'f': 'F',
   'g': 'G',
   'h': 'H',
   'i': 'I',
   'j': 'J',
   'k': 'K',
   'l': 'L'})]

Formattare

Se occorre formattare una struttura dati senza scriverla direttamente a un flusso (per delle registrazioni in un log, ad esempio) si usa pformat() per costruire una rappresentazione in formato stringa.

# pprint_pformat.py

import logging
from pprint import pformat
from pprint_data import data

logging.basicConfig(
    level=logging.DEBUG,
    format='%(levelname)-8s %(message)s',
)

logging.debug('Dati formattati per registrazione')
formatted = pformat(data)
for line in formatted.splitlines():
    logging.debug(line.rstrip())

La stringa formattata può essere indipendentemente stampata o registrata in un log.

$ python3 pprint_pformat.py

DEBUG    Dati formattati per registrazione
DEBUG    [(1, {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D'}),
DEBUG     (2,
DEBUG      {'e': 'E',
DEBUG       'f': 'F',
DEBUG       'g': 'G',
DEBUG       'h': 'H',
DEBUG       'i': 'I',
DEBUG       'j': 'J',
DEBUG       'k': 'K',
DEBUG       'l': 'L'})]

Classi Arbitrarie

La classe PrettyPrinter usata da pprint() può funzionare anche con classi personalizzate, se esse definiscono un metodo __repr__().

# pprint_arbitrary_object.py

from pprint import pprint


class node:

    def __init__(self, name, contents=[]):
        self.name = name
        self.contents = contents[:]

    def __repr__(self):
        return (
            'node(' + repr(self.name) + ', ' +
            repr(self.contents) + ')'
        )


trees = [
    node('nodo-1'),
    node('nodo-2', [node('nodo-2-1')]),
    node('nodo-3', [node('nodo-3-1')]),
]
pprint(trees)

Le rappresentazioni degli oggetti annidati sono combinate da PrettyPrinter per restituire l'intera rappresentazione in formato stringa.

$ python3 pprint_arbitrary_object.py

[node('nodo-1', []),
 node('nodo-2', [node('nodo-2-1', [])]),
 node('nodo-3', [node('nodo-3-1', [])])]

Ricorsione

Le strutture dati ricorsive sono rappresentate con un riferimento alla sorgente originale dei dati, nella forma <Ricorsione su nome del tipo con id=numero>.

# pprint_recursion.py

from pprint import pprint

local_data = ['a', 'b', 1, 2]
local_data.append(local_data)

print('id(local_data) =>', id(local_data))
pprint(local_data)

In questo esempio, la lista local_data è stata aggiunta a se stessa, creando un riferimento ricorsivo.

$ python3 pprint_recursion.py

id(local_data) => 140612206137800
['a', 'b', 1, 2, <Recursion on list with id=140612206137800>]

Limitare l'Output Nidificato

Per strutture dati molto profonde, si potrebbe non volere includere tutti i dettagli nell'output. I dati potrebbero non essere formattati in modo appropriato, il testo formattato potrebbe essere troppo grande per essere gestito oppure parte dei dati potrebbe essere non pertinente.

# pprint_depth.py

from pprint import pprint

from pprint_data import data

pprint(data, depth=1)

Il parametro depth può controllare quanto profondamente all'interno della struttura dati nidificata pretty printer si deve spingere. I livelli non inclusi sono rappresentati da ellissi.

$ python3 pprint_depth.py

[(...), (...)]

Controllare la Larghezza dell'Output

L'opzione predefinita di larghezza per il testo formattato è di 80 colonne. Per modificare la larghezza si usa il parametro width con pprint().

# pprint_width.py

from pprint import pprint

from pprint_data import data

for width in [80, 5]:
    print('LARGHEZZA =', width)
    pprint(data, width=width)
    print()

Quando la larghezza è insufficiente per accomodare la struttura dati formattata, le righe non sono troncate o divise in quanto la cosa potrebbe generare una sintassi non valida.

$ python3 pprint_width.py

LARGHEZZA = 80
[(1, {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D'}),
 (2,
  {'e': 'E',
   'f': 'F',
   'g': 'G',
   'h': 'H',
   'i': 'I',
   'j': 'J',
   'k': 'K',
   'l': 'L'})]

LARGHEZZA = 5
[(1,
  {'a': 'A',
   'b': 'B',
   'c': 'C',
   'd': 'D'}),
 (2,
  {'e': 'E',
   'f': 'F',
   'g': 'G',
   'h': 'H',
   'i': 'I',
   'j': 'J',
   'k': 'K',
   'l': 'L'})]

Vedere anche:

pprint
La documentazione della libreria standard per questo modulo.