嵌套类实例的有用默认__repr__方法

5

我有一个表示接口的抽象基类。该类的子类作为属性存储了该类的其他子类。

例如:

class AbstractBase(object):
    pass

class Child(AbstractBase):
    def __init__(self, cls1, cls2):
        assert isinstance(cls1, AbstractBase)
        assert isinstance(cls2, AbstractBase) # just to show they're instances

        self.cls1 = cls1
        self.cls2 = cls2

深度和布局的层次结构事先无法确定,但不会是递归的。

我可以在AbstractBase上放置什么作为__repr__,以便以有用的方式查看每个子类的属性?

我的当前方法是:

from pprint import pformat

class AbstractBase(object):
    def __repr__(self):
        return self.__class__.__name__ + '\n' \ 
                + pformat({k:v for k,v in self.__dict__.iteritems()
                           if not '__' in k})

对于一个没有任何属性是AbstractBase子类的基础类,这会输出可读性较高的内容,例如:

MyClass
{'var1': 1,
 'var2': 2}

然而,对于具有AbstractBase子类的类,它会出现问题,因为很难确定父类从哪里开始,另一个类从哪里结束(考虑到__repr__以上未给进一步缩进的更深层次的嵌套)。
如果像下面这样想象cls1和cls2有一个单独的int属性var,我会感到高兴:
Child
{'cls1': {
          'var': 1,
         },
 'cls2': {
          'var': 0,
         }
}

很遗憾,我不知道如何实现这个(或者它是否可能)。有什么想法吗?


2
你想要像 def __repr__(self, indent=1) 这样的东西,虽然可能无法与 pformat 结合使用,因此您必须自己编写一个。并且会有一些奇怪的边角情况是不能完全覆盖的。 - Karoly Horvath
2个回答

4

我喜欢用这种方式完成它:

class AbstractBase(object):
    def __repr__(self, indent=2):
        result = self.__class__.__name__ + '\n'
        for k,v in self.__dict__.iteritems():
            if k.startswith('__'):
                continue
            if isinstance(v, AbstractBase):
                vStr = v.__repr__(indent + 2)
            else:
                vStr = str(v)
            result += ' '*indent + k + ': ' + vStr
        result += '\n'
        return result

我喜欢这个回答。我稍微修改了一下,以考虑到序列,并打印出(我认为)更易读的输出 - 请看我的答案。如果没有其他人有更好的想法,我会在几天内接受你的答案 :) - sapi

1
这是对John Zwinck提出的内容略作修改后的版本。它考虑了如何格式化序列,并稍微改变了格式。但目前还不完美 - 我认为特别是对于字典,它将只打印键而无法打印值,因此可能会出现错误。
   def __repr__(self, indent=2):
        result = self.__class__.__name__ + '\n'
        items = self.__dict__.items()

        for i,(k,v) in enumerate(items):
            if '__' in k:   
                continue    
            if isinstance(v, AbstractBase):
                vStr = '\n' + ' '*(indent + 2) + v.__repr__(indent + 4)
            elif isinstance(v, collections.Iterable):
                s = str(v)
                bstart = s[0]
                bend = s[-1]

                newIndent = indent + 3
                vStr = '\n' + ' '*(newIndent - 1) + bstart
                for j,item in enumerate(v):
                    if isinstance(item, AbstractBase):
                        if j:
                            vStr += ' '*newIndent
                        vStr += item.__repr__(newIndent + 2)
                    else:    
                        vStr += repr(item)
                    vStr += ',\n'
                vStr += ' '*(newIndent - 1) + bend
            else:              
                vStr = str(v)  
            result += ' '*indent + k + ': ' + vStr

            if i != len(items) - 1:
                result += '\n'

        result = re.sub('\n+', '\n', result)
        return result  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接