Question

Is there any way to pass an "empty" numeric type into a numeric format specifier and have it print the blank space associated with that specifier?

ie.

n = "Scientific:"
v = 123.321
strfmt = "%5.4E"
output = "%s|"+strfmt+"|"
print output % (n, v)

>   Scientific:|1.2332E+02|

v = EMPTY
print output % (n, v)

>   Scientific:|          |

The ultimate goal is to deal with incomplete lines without adaptively changing the format string while looping

perline = 3
n = ["1st:","2nd:","3rd:","4th:","5th:","6th:"]
v = [1.0, 2.0, 3.0, 4.0]

strfmt = "%5.4E"
output = ("%s|"+strfmt+"|  ")*perline+"\n"

for args in map(EMPTY,*[iter(n),iter(v)]*perline):
    print output % args

>   1st:|1.0000E+00|  2nd:|2.0000E+00|  3rd:|3.0000E+00|
>   4th:|4.0000E+00|  5th:|          |  6th:|          |

To execute the above code I replaced EMPTY with None although that gives an error when you pass None to the format string

Was it helpful?

Solution 6

After viewing the responses it appears that there is no thing you can pass to a numeric format specifier to have it print the blank space associated with it.

There are a number of ways to work around this shown below. Personally I liked parts of Unutbu's answer. I think the way I will ultimately do it is create two format specifiers at the beginning and use logic as suggested by Blender.

OTHER TIPS

You won't be able to do it with just number formatting. You'll need some logic:

strfmt = "%5.4E"
v = None

if v in [None, '']:
  output = "%s|          |"
else:
  output = "%s|"+strfmt+"|"

Short answer: for a field n positions wide, '%​n​s' % '' will do. That is, to '%15s' % '' will print 15 spaces.

Long answer: when you specify a width in format specifiers, the first number is the minimum width of the entire field. "%5.4E" will take more than 5 places, though, because just the exponent will take 3 places already.

Specify something like "%12.4E" and get consistent results. For missing values, specify "%12s" and pass an empty string (not None) instead of the value.

One option would be to replace the numeric format in output with %s, and then in the replacing tuple create either the correct numeric output or the empty space depending on what v is:

>>> v = 123.321
>>> "%s|%s|" % (n, "%5.4E" % v if v is not None else " "*10)
'Scientific:|1.2332E+02|'
>>> v = None
>>> "%s|%s|" % (n, "%5.4E" % v if v is not None else " "*10)
'Scientific:|          |'

You could make this a little more functional with a simple closure:

def make_default_blank_formatter(format, default):
    def default_blank_formatter(v):
        return format % v if v is not None else default
    return default_blank_formatter

scientific_formatter = make_default_blank_formatter("%5.4E", " "*10)

>>> v = 123.321
>>> "%s|%s|" % (n, scientific_formatter(v))
'Scientific:|1.2332E+02|'
>>> v = None
>>> "%s|%s|" % (n, scientific_formatter(v))
'Scientific:|          |'

edit: Here is how you could rework your printing code to use this:

perline = 3
n = ["1st:","2nd:","3rd:","4th:","5th:","6th:"]
v = [1.0, 2.0, 3.0, 4.0, None, None]

strfmt = "%5.4E"
output = ("%s|%s|  ")*perline

for args in zip(*[iter(n),iter(map(scientific_formatter, v))]*perline):
    print output % args

You could use itertools.izip_longest to pair the items in n and v, and fill in missing values with the empty string.

To get the proper formatting for the items in v, you could pre-format the floats with strfmt, and use something like

output = "%s|%10s|  "

to join the items in n and v. By doing it this way, the items in v are now all strings, including the empty strings, and not a mixture of floats and empty strings.


import itertools

perline = 3
n = ["1st:", "2nd:", "3rd:", "4th:", "5th:", "6th:"]
v = [1.0, 2.0, 3.0, 4.0]

strfmt = "%5.4E"
v = (strfmt % val for val in v)
output = "%s|%10s|  "

pairs = itertools.izip_longest(n, v, fillvalue = '')
items = (output % (place, val) for place, val in pairs)
for row in zip(*[items]*perline):
    print(''.join(row))

yields

1st:|1.0000E+00|  2nd:|2.0000E+00|  3rd:|3.0000E+00|  
4th:|4.0000E+00|  5th:|          |  6th:|          |  

As suggested above, just convert your numbers to strings before printing, and replace missing numbers with empty strings of the equal length. This can be actually written very compact, however I would never use this in serious code:

from itertools import izip_longest, groupby, chain

perline = 3
n = ["1st:","2nd:","3rd:","4th:","5th:","6th:"]
v = [1.0, 2.0, 3.0, 4.0]

strfmt = "%5.4E"
output = ("%s|%s|  ")*perline

for _, args in groupby(enumerate(chain(*izip_longest(n, (strfmt % x for x in v),
                                                     fillvalue = ' '*len(strfmt % 0)))),
                       lambda (i, v): i/perline/2):
    print output % zip(*args)[1]

More readable and reliable version of the last loop:

l = ()
for pair in izip_longest(n, (strfmt % x for x in v),
                         fillvalue = ' '*len(strfmt % 0)):
    l += pair
    if len(l) == perline*2:
        print output % l
        l = ()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top