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 = ()