This is similar to How to print a list in Python “nicely”, but I would like to print the list even more nicely -- without the brackets and apostrophes and commas, and even b
It's useful to allow for uneven columns, without having to know in advance how many columns you can fit:
>>> words = [string.ascii_lowercase] + list(string.ascii_lowercase)
>>> print format_list(words)
abcdefghijklmnopqrstuvwxyz b d f h j l n p r t v x z
a c e g i k m o q s u w y
For your example:
>>> foolist = ['exiv2-devel', 'mingw-libs', 'tcltk-demos', 'fcgi',
... 'netcdf', 'pdcurses-devel', 'msvcrt', 'gdal-grass', 'iconv',
... 'qgis-devel', 'qgis1.1', 'php_mapscript']
>>> print format_list(foolist, spacing=4, width=31)
exiv2-devel msvcrt
mingw-libs gdal-grass
tcltk-demos iconv
fcgi qgis-devel
netcdf qgis1.1
pdcurses-devel php_mapscript
Here is the code. Note that it also handles words with ANSI color codes (such as from the colorama package) - they won't mess up the column widths.
ansi_pattern = re.compile(r'\x1b\[\d{1,2}m')
def get_nchars(string):
"""Return number of characters, omitting ANSI codes."""
return len(ansi_pattern.sub('', string))
def format_list(items, indent=0, spacing=2, width=79):
"""Return string listing items along columns.
items : sequence
List of items to display that must be directly convertible into
unicode strings. ANSI color codes may be present, and are taken
into account in determining column widths
indent : int
Number of spaces in left margin.
spacing : int
Number of spaces between columns.
width : int
Maximum number of characters per line, including indentation.
"""
if not items:
return u''
# Ensure all items are strings
items = [unicode(item) for item in items]
# Estimate number of columns based on shortest and longest items
minlen = min(get_nchars(item) for item in items)
maxlen = max(get_nchars(item) for item in items)
# Assume one column with longest width, remaining with shortest.
# Use negative numbers for ceiling division.
ncols = 1 - (-(width - indent - maxlen) // (spacing + min(1, minlen)))
ncols = max(1, min(len(items), ncols))
# Reduce number of columns until items fit (or only one column)
while ncols >= 1:
# Determine number of rows by ceiling division
nrows = -(-len(items) // ncols)
# Readjust to avoid empty last column
ncols = -(-len(items) // nrows)
# Split items into columns, and test width
columns = [items[i*nrows:(i+1)*nrows] for i in range(ncols)]
totalwidth = indent - spacing + sum(
spacing + max(get_nchars(item) for item in column)
for column in columns
)
# Stop if columns fit. Otherwise, reduce number of columns and
# try again.
if totalwidth <= width:
break
else:
ncols -= 1
# Pad all items to column width
for i, column in enumerate(columns):
colwidth = max(get_nchars(item) for item in column)
columns[i] = [
item + ' ' * (colwidth - get_nchars(item))
for item in column
]
# Transpose into rows, and return joined rows
rows = list(itertools.izip_longest(*columns, fillvalue=''))
return '\n'.join(
' ' * indent + (u' ' * spacing).join(row).rstrip()
for row in rows
)