Printing large nested data-structures in a neatly indented fashion is a common need. Python's pprint module supports indented printing of builtin data types like list. Unfortunately there is no support for custom types. The pprint module doesn't provide any extension hooks for custom types.
To solve this, I quickly put together an extensible pretty printer - xprint.py.
xprint knows how to print builtin types like list, tuple, str, int, etc. Additionally, xprint knows how to print objects of 3 new types - XObject, XField, and XSymbol. For example, XObject("Foo", 10, 20, "bar"), is printed as Foo(10, 20, "bar"). XField instances allow you to add labels - XObject("Foo", 10, XField("n",20)) is printed as Foo(10, n=20). XSymbol can be used to print strings without any quotes - XSymbol("Foo") is printed as Foo.
The basic idea for printing custom types is simple - convert the custom type to something understood by xprint, such as an XObject. The xprint module maintains a table of converters keyed by type. The xregister procedure can be used to register a converter.
Here's a quick example.
class Person(object) :
def __init__(self, name, age) :
self.name = name
self.age = age
from xprint import xprint, xregister, XObject, XField, XSymbol
def convert_person(x) :
return XObject("Person", x.name, x.age)
xregister(Person, convert_person)
To print, do the following.
>>> xprint(Person("Stepanov",40))
Person('Stepanov', 40)
You can also add a label for age like this.
def convert_person(x) :
return XObject("Person", x.name, XField("age",x.age))
xregister(Person, convert_person)
>>> xprint(Person("Stepanov",40))
Person('Stepanov', age=40)
Let's look at a bigger example - so that we can actually see the indentation in action!
The following code, constructs an expression tree representing the two roots of the quadratic equation ax2 + bx + c = 0.
First declare the required classes.
class BinaryOp(object) :
def __init__(self, expr1, expr2) :
self.expr1 = expr1
self.expr2 = expr2
class UnaryOp(object) :
def __init__(self, expr) :
self.expr = expr
class Add(BinaryOp) : pass
class Subtract(BinaryOp) : pass
class Multiply(BinaryOp) : pass
class Divide(BinaryOp) : pass
class Negate(UnaryOp) : pass
class SquareRoot(UnaryOp) : pass
class Variable(object) :
def __init__(self, name) :
self.name = name
Now construct expression trees representing the roots of a quadratic equation.
a,b,c = Variable('a'), Variable('b'), Variable('c')
sroot = SquareRoot(Subtract(Multiply(b,b), Multiply(4,Multiply(a,c))))
root1 = Divide(Add(Negate(b),sroot), Multiply(2,a))
root2 = Divide(Subtract(Negate(b),sroot), Multiply(2,a))
quadratic_roots = [root1, root2]
Before the roots can be printed, converters need to be registered for each custom type.
def convert_binary_op(x) :
class_name = type(x).__name__
return XObject(class_name, x.expr1, x.expr2)
xregister(Add, convert_binary_op)
xregister(Subtract, convert_binary_op)
xregister(Multiply, convert_binary_op)
xregister(Divide, convert_binary_op)
def convert_unary_op(x) :
class_name = type(x).__name__
return XObject(class_name, x.expr)
xregister(Negate, convert_unary_op)
xregister(SquareRoot, convert_unary_op)
xregister(Variable, lambda x : XSymbol(x.name))
Print the quadratic roots!
xprint(quadratic_roots)
[Divide(
Add(Negate(b),
SquareRoot(Subtract(Multiply(b, b), Multiply(4, Multiply(a, c))))),
Multiply(2, a)),
Divide(
Subtract(Negate(b),
SquareRoot(Subtract(Multiply(b, b), Multiply(4, Multiply(a, c))))),
Multiply(2, a))]
Now print with 50 columns. (The default is 80).
xprint(quadratic_roots, columns=50)
[Divide(
Add(Negate(b),
SquareRoot(
Subtract(Multiply(b, b),
Multiply(4, Multiply(a, c))))),
Multiply(2, a)),
Divide(
Subtract(Negate(b),
SquareRoot(
Subtract(Multiply(b, b),
Multiply(4, Multiply(a, c))))),
Multiply(2, a))]