File: LP6E/Chapter31/listtree.py

class ListTree:
    """
    Mix-in that returns a __str__ trace of the entire class tree and all
    its objects' attrs at and above the self instance.  The display is 
    run by print() automatically; use str() to fetch as a string.  This:

    -Uses __X pseudoprivate attr names to avoid conflicts with clients
    -Recurses to superclasses explicitly in DLFR (though not MRO) order
    -Uses __dict__ instead of dir() because attrs are listed per object
    -Supports classes with slots: lack of slots here ensures a __dict__
    """

    def __attrnames(self, obj, indent, unders=True):
        spaces = ' ' * (indent + 1)
        result = ''
        for attr in sorted(obj.__dict__):
            if attr.startswith('__') and attr.endswith('__'):
                if unders: result += f'{spaces}{attr}\n'
            else:
                result += f'{spaces}{attr}={getattr(obj, attr)!r}\n'
        return result

    def __listclass(self, aClass, indent):
        dots = '.' * indent
        preamble = (f'\n{dots}'
                    f'<Class {aClass.__name__}'
                    f', address {id(aClass):#x}')

        if aClass in self.__visited:
            return preamble + ': (see above)>\n'                # Already listed
        elif aClass is object:
            self.__visited[aClass] = True
            return preamble + ': (see dir(object))>\n'          # Skip object's 24
        else:
            self.__visited[aClass] = True
            here  = self.__attrnames(aClass, indent)            # My attrs + supers
            above = ''
            for Super in aClass.__bases__:
                above += self.__listclass(Super, indent + 4)
            return preamble + f':\n{here}{above}{dots}>\n'

    def __str__(self):
        self.__visited = {}
        here  = self.__attrnames(self, 0)                       # My attrs
        above = self.__listclass(self.__class__, 4)             # My supers tree
        return (f'<Instance of {self.__class__.__name__}'       # My class's name
                f', address {id(self):#x}'                      # My address (hex)
                f':\n{here}{above}>')                           # attrs + supers

if __name__ == '__main__':
    import testmixin
    testmixin.tester(ListTree)      # Test class in this module



[Home page] Books Code Blog Python Author Train Find ©M.Lutz