Home > database >  Printing complex object as multiline string
Printing complex object as multiline string

Time:07-13

I have very large settings for multiple applications and I want to print this as multi line string. Let me give example (simplified) and explain want I want to achieve and what I get. I think about use of some package to get such nice formatting.

I assume that constructors arguments are same to __dict__ or __slots__ - if not __dict__ or __slots__ is more important to show.

I wrote some formatting library for single line but maybe is better solution with multiline output and more options.

Update (important):

Please not suggest to customize __repr__ or __str__ - I can not or do not want to customize hundred of classes (especially from third party libraries).

class Endpoint:
    def __init__(self, host_or_ip: str, port: int):
        self.host_or_ip = host_or_ip
        self.port = port


class ConnectionSettings:
    def __init__(self, endpoints: list[Endpoint]):
        self.endpoints = endpoints


class FarmSettings:
    def __init__(self, name: str, connection_settings: ConnectionSettings):
        self.name = name
        self.connection_settings = connection_settings


def main():
    settings = FarmSettings(
        name='alfa',
        connection_settings=ConnectionSettings(
            endpoints=[
                Endpoint('localhost', 80)
            ]
        )
    )

    print(settings)
    
    # what I get from default __repr__
    #
    # <__main__.FarmSettings object at 0x00000203EF713AF0>
    #
    # what do I want from some method
    # FarmSettings(
    #     name='alfa',
    #     connection_settings=ConnectionSettings(
    #         endpoints=[
    #             Endpoint(name='localhost', port=80)
    #         ]
    #     )
    # )


if __name__ == '__main__':
    main()

CodePudding user response:

You could use e.g. __dict__ to recursively transform your objects to a dictionary, and then use pprint.pprint or json.dumps to pretty-print this dictionary:

def dictify(obj):
    if isinstance(obj, (int, float, str, bool)):
        return obj
    if isinstance(obj, (list, tuple)):
        return list(map(dictify, obj))
    return {"_type": type(obj).__name__,
            **{k: dictify(v) for k, v in obj.__dict__.items()}}
    

settings = FarmSettings(
    name='alfa',
    connection_settings=ConnectionSettings(
        endpoints=[
            Endpoint('localhost', 80)
        ]
    )
)

import pprint
pprint.pprint(dictify(settings))

Sample output:

{'_type': 'FarmSettings',
 'connection_settings': {'_type': 'ConnectionSettings',
                         'endpoints': [{'_type': 'Endpoint',
                                        'host_or_ip': 'localhost',
                                        'port': 80}]},
 'name': 'alfa'}

Note: The shown function is rudimentary at best. It does not cover many cases of attribute values (e.g. dict, let alone any kinds of more exotic classes), and it also does not handle cyclic references in your model (i.e. it will run into endless recursion in that case).

CodePudding user response:

Adding the __str__ (or __repr__) method to your class will allow you to overwrite the output of print(<insert complex object here>). For example:

def __str__(self):
   return self.<attribute>   ...

So, for example, if you had a class like this:

class Person:
   def __init__(self, name):
      self.name = name
   def __str__(self):
      return self.name

print(Person("Ryan"))

You get the output:

Ryan
  • Related