constructed stringuses __x attr names to avoid impacting clientsrecurses to superclasses explicitlyuses str format(for clarity""def __attrnames(selfobjindent)spaces (indent result 'for attr in sorted(obj __dict__)if attr startswith('__'and attr endswith('__')result +spaces '{ }\nformat(attrelseresult +spaces '{ }={ }\nformat(attrgetattr(objattr)return result def __listclass(selfaclassindent)dots indent if aclass in self __visitedreturn '\ { }\nformatdotsaclass __name__id(aclass)elseself __visited[aclasstrue here self __attrnames(aclassindentabove 'for super in aclass __bases__above +self __listclass(superindent+ return '\ { }\nformatdotsaclass __name__id(aclass)hereabovedotsdef __str__(self)self __visited {here self __attrnames(self above self __listclass(self __class__ return 'formatself __class__ __name__id(self)hereaboveif __name__ ='__main__'import testmixin testmixin tester(listtreethis class achieves its goal by traversing the inheritance tree--from an instance' __class__ to its classand then from the class' __bases__ to all superclasses recursivelyscanning each object' attribute __dict__ along the way ultimatelyit concatenates each tree portion' string as the recursion unwinds it can take while to understand recursive programs like thisbut given the arbitrary shape and depth of class treeswe really have no choice here (apart from explicit stack multiple inheritance"mix-inclasses