Empty numeric type into format specifer
-
29-04-2021 - |
質問
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
解決 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.
他のヒント
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, '%ns' % '' 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 = ()