Printing evenly spaced table from a list with a fo

2019-07-02 02:52发布

问题:

I'm sorry for asking this really elementary question, but I'm really stuck here... I've tried googling this, and using the search function, but can't find what I'm looking for.

I'm trying to print a table of values from a list of objects. But as the "name" string varies a lot in size, it skews the whole table, making it almost unreadable. I've tried adding tabs inbetween with \t, but the columns are still disaligned. Is there something I can add to this print statement to make a nice, straight table?

print "name","level","value"
for i in self.items:
    print i.name, i.lvl, i.value

回答1:

print "%20s %20s %20s" % ("name","level","value")
for i in self.items:
    print "%20s %20s %20s" % (i.name, i.lvl, i.value)

Add a format specifier. http://docs.python.org/2/library/stdtypes.html#string-formatting-operations



回答2:

You can try something like this:

In [1]: headers = ["name","level","value"]

In [2]: vals1 = ["Some long name", "a level", "a value"]

In [3]: vals2 = ["An even longer name", "another level", "another value"]

In [4]: max_lens = [len(str(max(i, key=lambda x: len(str(x))))) for i in zip(headers, vals1, vals2)]

In [5]: for row in (headers, vals1, vals2):
   ...:     print '|'.join('{0:{width}}'.format(x, width=y) for x, y in zip(row, max_lens))
   ...:
   ...:
name               |level        |value
Some long name     |a level      |a value
An even longer name|another level|another value

This finds the maximum length of the rows in your data and prints a table that is evenly spaced. In this case, max_lens uses zip to zip together all items in a given 'column' (think all items in the name column, for instance). Then, it finds the length of the longest string (as @Bakuriu points out, these need to be converted to strings in case any of the fields aren't strings) and stores that as the length of the 'column'. Then in the iteration, you specify a width which is going to be equal to the maximum length of that 'column', and pass in the value for that column on that row (hope that makes sense :) ).

The format method makes use of a the very powerful string formatting specification. This is a pretty basic example, but it can be modified to fit much more dynamic situations (such as those where you have numerous rows, etc.).

As for an example of how it could work with your data, you could try the below example. Note that this is not the most readable of code (which is important in Python), so if you were to do something similar, it may be worth it to actually write out some of the for loops so that it is a bit more obvious what is going on (since as I said, it is a bit obfuscated :) ):

In [1]: class MyClass(object):
   ...:     def __init__(self, a, b, c):
   ...:         self.name = a
   ...:         self.level = b
   ...:         self.value = c
   ...:
   ...:

In [2]: headers = ['name', 'level', 'value']

In [3]: vals1 = MyClass('Some long name', 'a level', 10348)

In [4]: vals2 = MyClass('An even longer name', 'another level', 100008484)

In [5]: items = (vals1, vals2)

In [6]: item_lens = [[getattr(item, x) for x in headers] for item in items]

In [7]: max_lens = [len(str(max(i, key=lambda x: len(str(x))))) for i in zip(*[headers] + item_lens)]

In [8]: print '|'.join('{0:{width}}'.format(x, width=y) for x, y in zip(headers, max_lens))
name               |level        |value

In [9]: for i in items:
   ...:     print '|'.join('{0:{width}}'.format(x, width=y) for x, y in zip([getattr(i, x) for x in headers], max_lens))
   ...:
   ...:
Some long name     |a level      |10348
An even longer name|another level|100008484


回答3:

If you do not have a maximum size for the items, and thus cannot use a format specifier, you can compute the current maximum length and use str.center(or str.rjust):

>>> from collections import namedtuple
>>> Record = namedtuple('Record', ['name', 'lvl', 'value'])
>>> items = [Record('a', 5, 1000), Record('aaaaaa', 15376576, 17.8), Record('aaaaaaaaaaaaaaaa', 7462, 1000)]
>>> max_name_length = len(max(items, key=(lambda x: len(x[0])))[0])
>>> max_lvl_length = len(str(max(items, key=(lambda x: len(str(x[1]))))[1]))
>>> max_value_length = len(str(max(items, key=(lambda x: len(str(x[2]))))[2]))
>>> for item in items:
...     print '%s %s %s' % (item.name.center(max_name_length),
...                         str(item.lvl).center(max_lvl_length),
...                         str(item.value).center(max_value_length))
... 
       a            5     1000
     aaaaaa      15376576 17.8
aaaaaaaaaaaaaaaa   7462   1000