[Python-3000] string.Formatter class
Eric Smith
eric+python-dev at trueblade.com
Tue Aug 28 13:48:24 CEST 2007
One of the things that PEP 3101 deliberately under specifies is the
Formatter class, leaving decisions up to the implementation. Now that a
working implementation exists, I think it's reasonable to tighten it up.
I have checked in a Formatter class that specifies the following methods
(in addition to the ones already defined in the PEP):
parse(format_string)
Loops over the format_string and returns an iterable of tuples
(literal_text, field_name, format_spec, conversion). This is used by
vformat to break the string in to either literal text, or fields that
need expanding. If literal_text is None, then expand (field_name,
format_spec, conversion) and append it to the output. If literal_text
is not None, append it to the output.
get_field(field_name, args, kwargs, used_args)
Given a field_name as returned by parse, convert it to an object to be
formatted. The default version takes strings of the form defined in the
PEP, such as "0[name]" or "label.title". It records which args have
been used in used_args. args and kwargs are as passed in to vformat.
convert_field(value, conversion)
Converts the value (returned by get_field) using the conversion
(returned by the parse tuple). The default version understands 'r'
(repr) and 's' (str).
Given these, we can define a formatter that uses the normal syntax, but
calls its arguments to get their value:
=================
class CallFormatter(Formatter):
def format_field(self, value, format_spec):
return format(value(), format_spec)
fmt = CallFormatter()
print(fmt.format('*{0}*', datetime.datetime.now))
=================
which prints:
*2007-08-28 07:39:29.946909*
Or, something that uses vertical bars for separating markup:
=================
class BarFormatter(Formatter):
# returns an iterable that contains tuples of the form:
# (literal_text, field_name, format_spec, conversion)
def parse(self, format_string):
for field in format_string.split('|'):
if field[0] == '+':
# it's markup
field_name, _, format_spec = field[1:].partition(':')
yield None, field_name, format_spec, None
else:
yield field, None, None, None
fmt = BarFormatter()
print(fmt.format('*|+0:^10s|*', 'foo'))
=================
which prints:
* foo *
Or, define your own conversion character:
=================
class XFormatter(Formatter):
def convert_field(self, value, conversion):
if conversion == 'x':
return None
if conversion == 'r':
return repr(value)
if conversion == 's':
return str(value)
return value
fmt = XFormatter()
print(fmt.format("{0!r}:{0!x}", fmt))
=================
which prints:
<__main__.XFormatter object at 0xf6f6d2cc>:None
These are obviously contrived examples, without great error checking,
but I think they demonstrate the flexibility. I'm not wild about the
method names, so any suggestions are appreciated. Any other comments
are welcome, too.
Eric.
More information about the Python-3000
mailing list