best way to implement custom pretty-printers
My solution was to replace pprint.PrettyPrinter with a simple wrapper that formats any floats it finds before calling the original printer.
from __future__ import divisionimport pprintif not hasattr(pprint,'old_printer'): pprint.old_printer=pprint.PrettyPrinterclass 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=MyPrettyPrinterdef 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)
This question may be a duplicate of:
Using pprint.PrettyPrinter
I looked through the source of pprint. It seems to suggest that, in order to enhance pprint()
, you’d need to:
- subclass
PrettyPrinter
- override
_format()
- test for
issubclass()
, - and (if it's not your class), pass back to
_format()
Alternative
I think a better approach would be just to have your own pprint()
, which defers to pprint.pformat
when it doesn't know what's up.
For example:
'''Extending pprint'''from pprint import pformatclass CrazyClass: passdef prettyformat(obj): if isinstance(obj, CrazyClass): return "^CrazyFoSho^" else: return pformat(obj)def prettyp(obj): print(prettyformat(obj))# testprettyp([1]*100)prettyp(CrazyClass())
The big upside here is that you don't depend on pprint
internals. It’s explicit and concise.
The downside is that you’ll have to take care of indentation manually.
If you would like to modify the default pretty printer without subclassing, you can use the internal _dispatch
table on the pprint.PrettyPrinter
class. You can see how examples of how dispatching is added for internal types like dictionaries and lists in the source.
Here is how I added a custom pretty printer for MatchPy's Operation type:
import pprintimport matchpydef _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
Now if I use pprint.pprint
on any object that has the same __repr__
as matchpy.Operation
, it will use this method to pretty print it. This works on subclasses as well, as long as they don't override the __repr__
, which makes some sense! If you have the same __repr__
you have the same pretty printing behavior.
Here is an example of the pretty printing some MatchPy operations now:
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'))))")))))