Package: printobject
Python version support: CPython 2.6, 2.7, 3.2, 3.3 and PyPy.Installation$pipinstallprintobjectUsageThe standard librarypprintmodule is great at visualizing all kinds of
built-in types like lists, dicts and tuples. But it does not attempt to
introspect user defined types. This is whereprintobjectcomes in. It
dumps the internals of any object as a dict, and pretty prints usingpprint.Key points:Any type of object can printed, but depending on the type the output
will be more or less insightful.Object introspection is based on the use ofdirrather than__dict__directly.Object attributes only include attributes owned by the object, omitting
class attributes.Callables are omitted when introspecting objects. The goal is to visualize
the data in objects.The synthetic attributes___name___and___type___(yes, that’s three
underscores!!!) are included in order to provide some metadata about the
object being printed.ModulesThis modules defines a number oftest_xxxfunctions at module level. They
are included in atestslist and visible in the output, but not listed
at top level because they are callables.>>>importsys>>>fromprintobjectimportpp>>>pp(sys.modules[__name__]){'___name___':'__main__','___type___':'<module{id0}>','__builtins__':<module'builtins'(built-in)>,'__cached__':None,'__file__':'/home/user/code/printobject/printobject/demos.py','__loader__':<_frozen_importlib.SourceFileLoaderobjectat0xb71c520c>,'__name__':'__main__','__package__':'printobject','absolute_import':_Feature((2,5,0,'alpha',1),(3,0,0,'alpha',0),16384),'defaults':('Module',),'re':<module're'from'/home/user/code/printobject/.tox/py33/lib/python3.3/re.py'>,'sys':<module'sys'(built-in)>,'tests':[<functiontest_moduleat0xb72d23d4>,<functiontest_classat0xb71c60bc>,<functiontest_instanceat0xb71c6104>,<functiontest_instance_collapsedat0xb71c614c>,<functiontest_class_oldat0xb71c6194>,<functiontest_instance_oldat0xb71c61dc>,<functiontest_instance_old_collapsedat0xb71c6224>,<functiontest_functionat0xb71c626c>,<functiontest_methodat0xb71c62b4>,<functiontest_lambdaat0xb71c62fc>,<functiontest_iterableat0xb71c6344>,<functiontest_generatorat0xb71c638c>]}Classes>>>classNode(object):...classatt='hidden'...def__init__(self,name):...self.name=name>>>fromprintobjectimportpp>>>pp(Node){'___name___':'Node','___type___':'<type{id0}>','__weakref__':{'___name___':'__weakref__','___type___':'<getset_descriptor{id1}>'},'classatt':"'hidden'"}InstancesObject graphs often aren’t fully acyclic. Where cycles exist it usually doesn’t
make sense to unroll them, so an object encountered more than once is displayed
with theduptag.  Objects also get assigned id’s, so that in the case
below it’s clear thatdup <Node {id0}>, which appears in therefsattribute ofc, is referring back toa.>>>a,b,c,d=Node('A'),Node('B'),Node('C'),Node('D')>>>a.refs=[b,d]>>>b.refs=[c]>>>c.refs=[a]>>>d.refs=[c]>>>fromprintobjectimportpp>>>pp(a){'___type___':'<Node{id0}>','name':"'A'",'refs':[{'___type___':'<Node{id1}>','name':"'B'",'refs':[{'___type___':'<Node{id2}>','name':"'C'",'refs':['dup <Node{id0}>']}]},{'___type___':'<Node{id3}>','name':"'D'",'refs':[{'___type___':'<Node{id2}>','name':"'C'",'refs':['dup <Node{id0}>']}]}]}In the example abovecis printed in expanded form twice, because both
occurrences are found at the same level of recursion. This can make the output
quite verbose if the same object is referenced numerous times, so an
alternative is to expand it only the first time and emitdupentries
subsequently, as shown below.>>>pp(a,collapse_duplicates=True){'___type___':'<Node{id0}>','name':"'A'",'refs':[{'___type___':'<Node{id1}>','name':"'B'",'refs':[{'___type___':'<Node{id2}>','name':"'C'",'refs':['dup <Node{id0}>']}]},{'___type___':'<Node{id3}>','name':"'D'",'refs':['dup <Node{id2}>']}]}Old style classes (Python 2.x only)>>>classNode():...classatt='hidden'...def__init__(self,name):...self.name=name>>>fromprintobjectimportpp>>>pp(Node){'___name___':'Node','___type___':'<classobj{id0}>','__module__':"'__main__'",'classatt':"'hidden'"}Old style instances (Python 2.x only)Instances of old style classes don’t look much different from
instances of new style classes. The difference is that they
identity asinstancetype, which is visible in the___type___value.>>>a,b,c,d=Node('A'),Node('B'),Node('C'),Node('D')>>>a.refs=[b,d]>>>b.refs=[c]>>>c.refs=[a]>>>d.refs=[c]>>>fromprintobjectimportpp>>>pp(a){'___type___':'<instance{id0}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'A'",'refs':[{'___type___':'<instance{id1}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'B'",'refs':[{'___type___':'<instance{id2}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'C'",'refs':['dup <instance{id0}>']}]},{'___type___':'<instance{id3}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'D'",'refs':[{'___type___':'<instance{id2}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'C'",'refs':['dup <instance{id0}>']}]}]}In collapsed form:>>>pp(a,collapse_duplicates=True){'___type___':'<instance{id0}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'A'",'refs':[{'___type___':'<instance{id1}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'B'",'refs':[{'___type___':'<instance{id2}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'C'",'refs':['dup <instance{id0}>']}]},{'___type___':'<instance{id3}>','__module__':"'__main__'",'classatt':"'hidden'",'name':"'D'",'refs':['dup <instance{id2}>']}]}CallablesCallables can also be printed, but they are less interesting since they
have no public attributes.Functions:>>>fromprintobjectimportpp>>>pp(pp){'___name___':'pp','___type___':'<function{id0}>'}Methods:>>>fromprintobjectimportDumper>>>pp(Dumper.dump){'___name___':'dump','___type___':'<instancemethod{id0}>'}Lambdas:>>>pp(lambdax:x){'___name___':'<lambda>','___type___':'<function{id0}>'}IterablesIterables are printed using their normal__repr__. In this case
there are no___type___and___name___attributes synthesized
in the output.>>>it=frozenset(range(10))>>>fromprintobjectimportpp>>>pp(it)['0','1','2','3','4','5','6','7','8','9']GeneratorsGenerators are a special case of iterables, because the values are
created dynamically. Printing generators isn’t insightful without
unrolling them, so they will be materialized first. But this means that
if the generator is infinite the function will never return.>>>gen=(xforxinrange(10))>>>fromprintobjectimportpp>>>pp(gen)['0','1','2','3','4','5','6','7','8','9']