There are currently three types of functionality in Python that are equivalent to C's sprintf. (Exclude f
-string because it doesn't use a format string)
name? | How to use |
---|---|
% -formatting |
fmt % values |
str.format() |
fmt.format(*values) / fmt.format(**values) |
Template strings | string.Template(fmt).substitute(**values) |
This article summarizes how to find out how many tuple elements or dictionary keys are required in the values
part of the table above.
To be honest, I think it's a fairly infrequently used knowledge, but it may be useful when you make your own Formatter by inheriting logging.Formatter
.
--%
-formatting → Fail format and see exception.
str.format
→ string.Formatter().parser(fmt)
string.Template.pattern.finditer(fmt)
%
-formatting (printf style formatting)
Unlike the other two, I can't find a dedicated function for parsing in the standard package, so I'll do my best.
This time I made a function to decipher based on the exception thrown back. If the type requested in the values
part of fmt% values
is a tuple, its length is returned, and if it is a dictionary, its key is returned.
def parse_printf_style_format(fmt):
if not isinstance(fmt, (bytes, str)):
raise TypeError('got ' + type(fmt).__name__)
try:
fmt % ()
except TypeError as e:
if e.args[0] == 'not enough arguments for format string':
values = ()
elif e.args[0] == 'format requires a mapping':
values = {}
else:
raise
else:
return None
if isinstance(values, tuple):
while True:
try:
fmt % values
except TypeError as e:
if e.args[0] == 'not enough arguments for format string':
values += (0,)
else:
raise ValueError('invalid format: ' + repr(fmt))
else:
return len(values)
elif isinstance(values, dict):
while True:
try:
fmt % values
except TypeError as e:
if e.args[0] == 'not enough arguments for format string':
raise ValueError('invalid format: ' + repr(fmt))
else:
raise
except KeyError as e:
values[e.args[0]] = 0
else:
return tuple(values.keys())
else:
assert False
Example of use
>>> parse_printf_style_format('%d %s %x')
3
>>> parse_printf_style_format('%(foo)s, %(bar)d')
('foo', 'bar')
str.format()
It's a blow with string.Formatter (). parse (fmt)
. For details, refer to Official Document.
>>> import string
>>> fmt = '{foo:s}, {{bar:d}}, and {:f}'
>>> list(string.Formatter().parse(fmt))
[('', 'foo', 's', None),
(', {', None, None, None),
('bar:d}', None, None, None),
(', and ', '', 'f', None)]
Template strings
It is a blow with string.Template.pattern.finditer (fmt)
.
The regular expression that matches the placeholder is stored in the pattern
attribute.
>>> import string
>>> fmt = '${this_is_braced} $$this_is_escaped $@this_is_invalid $this_is_named'
>>> print(string.Template.pattern.pattern)
\$(?:
(?P<escaped>\$) | # Escape sequence of two delimiters
(?P<named>[_a-z][_a-z0-9]*) | # delimiter and a Python identifier
{(?P<braced>[_a-z][_a-z0-9]*)} | # delimiter and a braced identifier
(?P<invalid>) # Other ill-formed delimiter exprs
)
>>> [match.groupdict() for match in string.Template.pattern.finditer(fmt)]
[{'braced': 'this_is_braced', 'escaped': None, 'invalid': None, 'named': None},
{'braced': None, 'escaped': '$', 'invalid': None, 'named': None},
{'braced': None, 'escaped': None, 'invalid': '', 'named': None},
{'braced': None, 'escaped': None, 'invalid': None, 'named': 'this_is_named'}]
The cold treatment of %
-formatting is terrible.
Recommended Posts