pprint — Impresión bonita de estructuturas de datos

Propósito:Impresión bonita de estructuras de datos

El módulo pprint contiene una «impresora bonita» para producir vistas estéticamente agradables de estructuras de datos. El formateador produce representaciones de estructuras de datos que pueden ser analizadas correctamente por el intérprete, y que también son fáciles para ser leídas por un humano. La salida se mantiene en una sola línea, si es posible, y con sangría cuando se divide en varias líneas.

Los ejemplos en esta sección dependen de pprint_data.py, que se muestra aquí.

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'}),
    (3, ['m', 'n']),
    (4, ['o', 'p', 'q']),
    (5, ['r', 's', 't''u', 'v', 'x', 'y', 'z']),
]

Imprimiendo

La forma más sencilla de usar el módulo es a través de la función pprint().

pprint_pprint.py
from pprint import pprint

from pprint_data import data

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

pprint() formatea un objeto y lo escribe en la secuencia de datos pasado como un argumento (o sys.stdout por defecto).

$ python3 pprint_pprint.py

PRINT:
[(1, {'c': 'C', 'b': 'B', 'd': 'D', 'a': 'A'}), (2, {'k': 'K', 'i':
'I', 'g': 'G', 'f': 'F', 'e': 'E', 'h': 'H', 'l': 'L', 'j': 'J'}), (
3, ['m', 'n']), (4, ['o', 'p', 'q']), (5, ['r', 's', 'tu', 'v', 'x',
 'y', 'z'])]

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'}),
 (3, ['m', 'n']),
 (4, ['o', 'p', 'q']),
 (5, ['r', 's', 'tu', 'v', 'x', 'y', 'z'])]

Formateando

Para formatear una estructura de datos sin escribirla directamente a una corriente (por ejemplo, para el registro), usa pformat() para construir una representación cadena .

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('Logging pformatted data')
formatted = pformat(data)
for line in formatted.splitlines():
    logging.debug(line.rstrip())

La cadena formateada se puede imprimir o registrar de forma independiente.

$ python3 pprint_pformat.py

DEBUG    Logging pformatted data
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'}),
DEBUG     (3, ['m', 'n']),
DEBUG     (4, ['o', 'p', 'q']),
DEBUG     (5, ['r', 's', 'tu', 'v', 'x', 'y', 'z'])]

Clases arbitrarias

La clase PrettyPrinter utilizada por pprint() también puede funcionar con clases personalizadas, si definen un método __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('node-1'),
    node('node-2', [node('node-2-1')]),
    node('node-3', [node('node-3-1')]),
]
pprint(trees)

Las representaciones de los objetos anidados se combinan por el PrettyPrinter para devolver la representación de cadena completa.

$ python3 pprint_arbitrary_object.py

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

Recursión

Las estructuras de datos recursivas están representadas con una referencia a la fuente original de los datos, dada en el formato <Recursion on typename with id=number>.

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)

En este ejemplo, la lista local_data se agrega a sí misma, creando una referencia recursiva.

$ python3 pprint_recursion.py

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

Limitar la salida anidada

Para estructuras de datos muy profundas, puede no ser deseable incluir todos los detalles para la salida. Es posible que los datos no estén formateados correctamente, el texto formateado puede ser demasiado grande para manejar, o algunos de los datos pueden ser extraños.

pprint_depth.py
from pprint import pprint

from pprint_data import data

pprint(data, depth=1)
pprint(data, depth=2)

Usa el argumento depth para controlar qué tan abajo en la estructura de datos anidados la impresora bonita recurses. Niveles no incluidos en el resultado son representados por puntos suspensivos.

$ python3 pprint_depth.py

[(...), (...), (...), (...), (...)]
[(1, {...}), (2, {...}), (3, [...]), (4, [...]), (5, [...])]

Controlando el ancho de salida

El ancho de salida predeterminado para el texto formateado es de 80 columnas. Para ajustar ese ancho, usa el argumento width para pprint().

pprint_width.py
from pprint import pprint

from pprint_data import data

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

Cuando el ancho es demasiado pequeño para acomodar la estructura de datos formateados, las líneas no se truncan ni envuelven si al hacerlo se introduce sintaxis inválida

$ python3 pprint_width.py

WIDTH = 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'}),
 (3, ['m', 'n']),
 (4, ['o', 'p', 'q']),
 (5, ['r', 's', 'tu', 'v', 'x', 'y', 'z'])]

WIDTH = 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'}),
 (3,
  ['m',
   'n']),
 (4,
  ['o',
   'p',
   'q']),
 (5,
  ['r',
   's',
   'tu',
   'v',
   'x',
   'y',
   'z'])]

El marcador compact le dice a pprint() que intente encajar más datos en cada línea, en lugar de diseminar estructuras de datos complejas a través de líneas.

pprint_compact.py
from pprint import pprint

from pprint_data import data

print('DEFAULT:')
pprint(data, compact=False)
print('\nCOMPACT:')
pprint(data, compact=True)

Este ejemplo muestra que cuando una estructura de datos no cabe en una línea, es dividida (como con el segundo elemento en la lista de datos). Cuando múltiples elementos pueden caber en una línea, como con el tercero y cuarto miembros, se colocan de esa manera.

$ python3 pprint_compact.py

[(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'}),
 (3, ['m', 'n']),
 (4, ['o', 'p', 'q']),
 (5, ['r', 's', 'tu', 'v', 'x', 'y', 'z'])]
[(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'}),
 (3, ['m', 'n']), (4, ['o', 'p', 'q']),
 (5, ['r', 's', 'tu', 'v', 'x', 'y', 'z'])]