Using .format() to format a list with field width arguments

Solution 1:

The error message

ValueError: cannot switch from manual field specification to automatic field numbering

pretty much says it all: You need to give explicit field indices everwhere, and

print('{0[0]:{1}s} {0[1]:{2}d} {0[2]:{3}f}'.format(res, 10, 5, .2))

works fine.

Solution 2:

If you want to use .format(res, args), you can specify all indices in the format string like that:

>>> print('{0[0]:{1[0]}s} {0[1]:{1[1]}d} {0[2]:{1[2]}f}'.format(res, args))
Irene Adler    35 24.80

But, if you want to make the format string without indices, you can create a tuple of consecutive pairs (res[0], args[0], ... , res[-1], args[-1]).

This is done by this idiom:

>>> sum(zip(res, args), ())
('Irene Adler', 10, 35, 5, 24.798, 0.2)

You can now pass this into a simplified format string (via *-argument):

>>> fmt = sum(zip(res, args), ())
>>> print('{:{}s} {:{}d} {:{}f}'.format(*fmt))
('Irene Adler', 10, 35, 5, 24.798, 0.2)

This can, of course, be done on one line:

>>> print('{:{}s} {:{}d} {:{}f}'.format(*sum(zip(res, args), ())))
Irene Adler    35 24.80

To make it readable, I would name the transformation:

def flat_pairs(iterable1, iterable2):
    return sum(zip(iterable1, iterable2), ())

# (...)

>>> print('{:{}s} {:{}d} {:{}f}'.format(*flat_pairs(res, args)))
Irene Adler    35 24.80

I think the last one is a reasonable trade-off between readability, convenience, and of course - showing off your Pythonic way of thinking, which is the primary motivation for playing with such stuff.