4

Having trouble printing a namedtuple:

Info = namedtuple('Info', ['type', 'value', 'x', 'y'])

so that the values are aligned and with space(padding) between them, for example like so:

Info( type='AAA',    value=80000,   x=16.4,   y=164.2 )
Info( type='A',      value=78,      x=1.5,    y=11.3  )
Info( type='ABBCD',  value=554,     x=11.7,   y=10.1  )
Info( type='AFFG',   value=1263,    x=121.3,  y=12.8  )

Ideally, without the commas. I have tried pprint and tried printing using the _asdict with no success as suggested here. Same with the format which I could not make it to behave with named tuples. Any ideas or example codes?

Community
  • 1
  • 1
Yannis
  • 1,682
  • 7
  • 27
  • 45
  • Why would you want that? – jonrsharpe Apr 09 '16 at 15:39
  • 1
    I am printing some results on the terminal which I need to be readable and 'pretty'. Also, the main idea is that I would output that nicely-formatted namedtuple result onto a file. – Yannis Apr 09 '16 at 15:41
  • I would argue that that is neither nice nor pretty! – jonrsharpe Apr 09 '16 at 15:43
  • 1
    I can see what you mean, but still, arguably it is slightly clearer to read since it has a table-like format. It would be great if there was a way to imitate that on the terminal and when writing it onto a text file. – Yannis Apr 09 '16 at 15:45
  • 3
    Maybe some of the various libraries to print a plaintext table could help you? https://pypi.python.org/pypi/terminaltables – ThiefMaster Apr 09 '16 at 15:45
  • 1
    The problem with this is when the first item prints it needs to know about subsequent items, to know how much to indent. So you have to print all as a collection, you can't just print one on its own. That's ugly! – wim Apr 09 '16 at 15:53

2 Answers2

5

Here is my implementation of a pretty print for named tuples:

def prettyprint_namedtuple(namedtuple,field_spaces):
    assert len(field_spaces) == len(namedtuple._fields)
    string = "{0.__name__}( ".format(type(namedtuple))
    for f_n,f_v,f_s in zip(namedtuple._fields,namedtuple,field_spaces):
        string+= "{f_n}={f_v!r:<{f_s}}".format(f_n=f_n,f_v=f_v,f_s=f_s)
    return string+")"

gives the output I believe you were looking for:

a = Info( type='AAA',    value=80000,   x=16.4,   y=164.2 )
b = Info( type='A',      value=78,      x=1.5,    y=11.3  )
c = Info( type='ABBCD',  value=554,     x=11.7,   y=10.1  )
d = Info( type='AFFG',   value=1263,    x=121.3,  y=12.8  )

tups = [a,b,c,d]

for t in tups:
    print(prettyprint_namedtuple(t,(10, 9, 8, 6)))

output:

Info( type='AAA'     value=80000    x=16.4    y=164.2 )
Info( type='A'       value=78       x=1.5     y=11.3  )
Info( type='ABBCD'   value=554      x=11.7    y=10.1  )
Info( type='AFFG'    value=1263     x=121.3   y=12.8  )
Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
2

Because you need to know the field widths ahead of time, the only reasonable solution I can suggest for you is just to write a helper function to convert to the format you want.

def namedtuple_to_str(t, field_widths=15):
    if isinstance(field_widths, int):
        field_widths = [field_widths] * len(t)
    field_pairs = ['{}={}'.format(field, getattr(t, field)) for field in t._fields]
    s = ' '.join('{{:{}}}'.format(w).format(f) for w,f in zip(field_widths, field_pairs))
    result = '{}( {} )'.format(type(t).__name__, s)
    return result

Demo:

>>> from collections import namedtuple
>>> Info = namedtuple('Info', ['type', 'value', 'x', 'y'])
>>> t = Info(type='AAA', value=80000, x=16.4, y=164.2)
>>> 
>>> print namedtuple_to_str(t)
Info( type=AAA        value=80000     x=16.4          y=164.2         )
>>> print namedtuple_to_str(t, field_widths=11)
Info( type=AAA    value=80000 x=16.4      y=164.2     )
>>> print namedtuple_to_str(t, field_widths=[10, 20, 7, 7])
Info( type=AAA   value=80000          x=16.4  y=164.2 )

To print a collection of these, it is not hard to pre-compute the field widths you want by using max(..., key=len).

wim
  • 338,267
  • 99
  • 616
  • 750