python - La mejor manera de implementar impresoras bonitas personalizadas.
Personalizando pprint.PrettyPrinter
La documentación para el módulo pprint
menciona que el método PrettyPrinter.format
está destinado a hacer posible personalizar el formato.
Supongo que es posible anular este método en una subclase, pero esto no parece proporcionar una manera de hacer que los métodos de la clase base apliquen el ajuste de línea y la sangría.
- ¿Me estoy perdiendo de algo?
- ¿Hay una mejor manera de hacerlo (por ejemplo, otro módulo)?
¿Alternativas?
He comprobado el pretty
módulo, que parece interesante, pero no parece proporcionar una forma de personalizar el formato de las clases de otros módulos sin modificar esos módulos.
Creo que lo que estoy buscando es algo que me permita proporcionar una asignación de tipos (o quizás funciones) que identifique los tipos a las rutinas que procesan un nodo. Las rutinas que procesan un nodo tomarían un nodo y devolverían la cadena que lo representa, junto con una lista de nodos secundarios. Y así.
¿Por qué estoy buscando en la impresión bonita
Mi objetivo final es imprimir de forma compacta las secciones con formato personalizado de un xml.etree.ElementTree
formato xml.etree.ElementTree
.
(Me sorprendió no encontrar más soporte Python para DocBook. Tal vez me perdí algo allí).
Construí algunas funciones básicas en un cliente llamado xmlearn que usa lxml . Por ejemplo, para volcar un archivo de Docbook, podría:
xmlearn -i docbook_file.xml dump -f docbook -r book
Es bastante medio, pero me dio la información que estaba buscando.
xmlearn tiene otras características, como la capacidad de construir una imagen gráfica y hacer volcados que muestran las relaciones entre las etiquetas en un documento XML. Estos son casi totalmente ajenos a esta pregunta.
También puede realizar un volcado a una profundidad arbitraria, o especificar un XPath como un conjunto de puntos de partida. El tipo de XPath hace que el formato específico del docbook quede obsoleto, por lo que no está realmente bien desarrollado.
Esto todavía no es realmente una respuesta para la pregunta. Todavía estoy esperando que haya una impresora bonita fácilmente personalizable por ahí en algún lugar.
Mi solución fue reemplazar pprint.PrettyPrinter con un envoltorio simple que formatea cualquier flota que encuentre antes de llamar a la impresora original.
from __future__ import division
import pprint
if not hasattr(pprint,''old_printer''):
pprint.old_printer=pprint.PrettyPrinter
class MyPrettyPrinter(pprint.old_printer):
def _format(self,obj,*args,**kwargs):
if isinstance(obj,float):
obj=round(obj,4)
return pprint.old_printer._format(self,obj,*args,**kwargs)
pprint.PrettyPrinter=MyPrettyPrinter
def pp(obj):
pprint.pprint(obj)
if __name__==''__main__'':
x=[1,2,4,6,457,3,8,3,4]
x=[_/17 for _ in x]
pp(x)
Si desea modificar la impresora bonita predeterminada sin _dispatch
subclases, puede usar la tabla _dispatch
interna en la clase pprint.PrettyPrinter
. Puede ver cómo ejemplos de cómo se agrega el envío para tipos internos como diccionarios y listas en la fuente .
Aquí es cómo agregué una impresora bonita personalizada para el tipo de operación de MatchPy :
import pprint
import matchpy
def _pprint_operation(self, object, stream, indent, allowance, context, level):
"""
Modified from pprint dict https://github.com/python/cpython/blob/3.7/Lib/pprint.py#L194
"""
operands = object.operands
if not operands:
stream.write(repr(object))
return
cls = object.__class__
stream.write(cls.__name__ + "(")
self._format_items(
operands, stream, indent + len(cls.__name__), allowance + 1, context, level
)
stream.write(")")
pprint.PrettyPrinter._dispatch[matchpy.Operation.__repr__] = _pprint_operation
Ahora, si uso pprint.pprint
en cualquier objeto que tenga la misma __repr__
que matchpy.Operation
, usará este método para imprimirlo bastante. Esto también funciona en las subclases, siempre que no anulen el __repr__
, ¡lo cual tiene sentido! Si tiene el mismo __repr__
tiene el mismo comportamiento de impresión bonito.
Aquí hay un ejemplo de la bonita impresión de algunas operaciones MatchPy ahora:
ReshapeVector(Vector(Scalar(''1'')),
Vector(Index(Vector(Scalar(''0'')),
If(Scalar(''True''),
Scalar("ReshapeVector(Vector(Scalar(''2''), Scalar(''2'')), Iota(Scalar(''10'')))"),
Scalar("ReshapeVector(Vector(Scalar(''2''), Scalar(''2'')), Ravel(Iota(Scalar(''10''))))")))))
Esta pregunta puede ser un duplicado de:
Usando pprint.PrettyPrinter
Miré a través de la fuente de pprint . Parece sugerir que, para mejorar pprint()
, deberías:
- subclase
PrettyPrinter
- anular
_format()
- prueba para
issubclass()
, - y (si no es su clase), regrese a
_format()
Alternativa
Creo que un mejor enfoque sería tener tu propia pprint()
, que difiere de pprint.pformat
cuando no sabe lo que pasa.
Por ejemplo:
''''''Extending pprint''''''
from pprint import pformat
class CrazyClass: pass
def prettyformat(obj):
if isinstance(obj, CrazyClass):
return "^CrazyFoSho^"
else:
return pformat(obj)
def prettyp(obj):
print(prettyformat(obj))
# test
prettyp([1]*100)
prettyp(CrazyClass())
La gran ventaja aquí es que no dependes de los pprint
internos de la pprint
. Es explícito y conciso.
El inconveniente es que tendrás que cuidar la sangría manualmente.