Following a remark here: How to define a new string formatter, I tried subclassing string.Formatter
. Here is what I've done. Unfortunately I seem to have broken it in the process
import string
from math import floor, log10
class CustFormatter(string.Formatter):
"Defines special formatting"
def __init__(self):
super(CustFormatter, self).__init__()
def powerise10(self, x):
if x == 0: return 0, 0
Neg = x < 0
if Neg: x = -x
a = 1.0 * x / 10**(floor(log10(x)))
b = int(floor(log10(x)))
if Neg: a = -a
return a, b
def eng(self, x):
a, b = self.powerise10(x)
if -3 < b < 3: return "%.4g" % x
a = a * 10**(b%3)
b = b - b%3
return "%.4g*10^%s" % (a, b)
def format_field(self, value, format_string):
# handle an invalid format
if format_string == "i":
return self.eng(value)
else:
return super(CustFormatter,self).format_field(value, format_string)
fmt = CustFormatter()
print('{}'.format(0.055412))
print(fmt.format("{0:i} ", 55654654231654))
print(fmt.format("{} ", 0.00254641))
As if as in the last line, I don't refer to the variables by position, I get a KeyError
. It is obviously expecting a key which is optional in the original class but I don't understand why and I am not sure what I've done wrong.
str.format
does auto numbering, while string.Formatter
does not.
Modifying __init__
and overriding get_value
will do the trick.
def __init__(self):
super(CustFormatter, self).__init__()
self.last_number = 0
def get_value(self, key, args, kwargs):
if key == '':
key = self.last_number
self.last_number += 1
return super(CustFormatter, self).get_value(key, args, kwargs)
BTW, above code does not strictly mimic str.format
behavior. str.format
complains if we mix auto numbering with manual number, but above does not.
>>> '{} {1}'.format(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: cannot switch from automatic field numbering to manual field specification
>>> '{0} {}'.format(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: cannot switch from manual field specification to automatic field numbering
Good news: you have not done anything wrong.
Bad news: that's how string.Formatter
behaves, it does not support {}
-like positional format. So, the last call will fail even without any subclassing. Good news: that's can be fixed by overriding the parse
method:
import string
class CF(string.Formatter):
def parse(self, s):
position = 0
for lit, name, spec, conv in super(CF, self).parse(s):
if not name:
name = str(position)
position += 1
yield lit, name, spec, conv
Bad news... Ah, no, that's basically it:
>>> CF().format('{} {}!', 'Hello', 'world')
'Hello world!'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With