Python漂亮地打印嵌套对象

5

我有三个Python类A、B和C。A包含B对象,而B包含C的对象。我的目标是在打印A类对象时,它应该以以下格式进行漂亮的打印。 C类中也可以有更多的嵌套。

A:
    loc : XYZ
    qual : ABC
    b :
        name : ABC
        age : 30
        c :
            address : ABC
            phn : 99009

以下是供参考的类。
class C(object):
    def __init__(self):
        self.address='ABC'
        self.phn=99009

class B(object):
    def __init__(self):
        self.name='ABC'
        self.age=30
        self.c = C()

class A(object):
    def __init__(self):
        self.loc = 'XYZ'
        self.qual = 'ABC'
        self.b = B()

2
既然您提到了美化打印,并使用标签 pretty-print,我就假设您只是想控制 pprint 模块如何处理您的对象?最简单的方法是创建一个自定义的 PrettyPrinter 子类,覆盖文档中记录的方法,以处理您的对象为类似于列表和字典的“递归对象”。然后,您只需在 pprint 模块的位置上使用该类的一个实例即可。 - abarnert
3个回答

9
以下方法是让您的类继承一个实现了 __str__ 方法的共同基类:
class PrettyPrinter(object):
    def __str__(self):
        lines = [self.__class__.__name__ + ':']
        for key, val in vars(self).items():
            lines += '{}: {}'.format(key, val).split('\n')
        return '\n    '.join(lines)

class C(PrettyPrinter):
    def __init__(self):
        self.address='ABC'
        self.phn=99009

class B(PrettyPrinter):
    def __init__(self):
        self.name='ABC'
        self.age=30
        self.c = C()

class A(PrettyPrinter):
    def __init__(self):
        self.loc = 'XYZ'
        self.qual = 'ABC'
        self.b = B()

a = A()
print(a)

在Python 3.6及更高版本中,它的显示方式如下:
A:
    loc: XYZ
    qual: ABC
    b: B:
        name: ABC
        age: 30
        c: C:
            address: ABC
            phn: 99009

请注意,所有属性都会自动打印出来。它们的打印顺序由“vars”函数确定,该函数实际上查找“__dict__”字典。在Python 3.5及以下版本中,此字典具有不确定的顺序,因此打印输出不如3.6及更高版本那样好看。

6
以下递归函数使用类的__dict__属性从头开始获取类属性的key:value对。从这里开始,我们只需测试该值是否是另一个类(在这种情况下,我们再次调用自己),否则我们只需按所需格式打印它。
唯一需要跟踪的是当前缩进级别,即此行当前正在打印的级别。这可以通过一个默认参数(indent)轻松完成,我们在每次递归之前增加它即可。
def pretty_print(clas, indent=0):
    print(' ' * indent +  type(clas).__name__ +  ':')
    indent += 4
    for k,v in clas.__dict__.items():
        if '__dict__' in dir(v):
            pretty_print(v,indent)
        else:
            print(' ' * indent +  k + ': ' + str(v))

它可以正常工作:

>>> pretty_print(A())
A:
    loc: XYZ
    qual: ABC
    B:
        name: ABC
        age: 30
        C:
            address: ABC
            phn: 99009

正是我所需要的,谢谢。 - slashdottir
我不是提问者。 - slashdottir
@slashdottir 哦,好的。 - Joe Iddon
这个PrettyPrinter的东西很好,但大多数时候你只需要读取输出。感谢您帮我省去了一个头疼的问题。+1 - Edward

-1

如果你:

  1. 嵌入一个列表或字典
  2. 想要一个字符串结果而不是stdout.write
class C(object):
    def __init__(self):
        self.address='ABC'
        self.phn=99009
        self.my_list = [1, 2, 3, 4]
        self.my_dic = {1:2, 3:4}

使用 print(str_pretty(C(), indent=2, key="self"))

c: (C)
    address: ABC
    phn: 99009
    my_list: (list)
    0: 1
    1: 2
    2: 3
    3: 4
    my_dic: (dict)
    1: 2
    3: 4

def str_pretty(obj, indent=1, rec=0, key=''):
    """Returns: pretty str of an object
    obj <- the object to print
    indent <- the indent per depth
    rec <- used in recursion
    """
    # Init
    s_indent = ' ' * indent * rec
    items = {}
    stg = s_indent

    if key != '': stg += str(key) + ': '

    # Discriminate && Check if final
    if isinstance(obj, list):
        items = enumerate(obj)
    elif isinstance(obj, dict):
        items = obj.items()
    elif '__dict__' in dir(obj):
        items = obj.__dict__.items()
    if not items:
        return stg + str(obj)

    # Recurse
    stg += '(' + type(obj).__name__ + ')\n'
    for k, v in items:
        stg += str_pretty(v, indent=indent, rec=rec+1, key=k) + "\n"

    # Return without empty lines
    return re.sub(r'\n\s*\n', '\n', stg)[:-1]

无法工作。TypeError:不能将序列乘以非整数类型的“str”。 - Hugues Gauthier

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