string.Formatter
On its own, Formatter().format(s, ...) gives the same result as s.format(...). Its point is subclassing: get_value, get_field, convert_field, format_field, parse and check_unused_args are hooks you replace to change one part of the pipeline.
Demo
import string string.Formatter().format('{} and {} by {name}', *['tea', 'cake'], name='Ada')
The values here are all str, so numeric specs such as :d fail. '{name[-1]}' does not mean the last character: -1 is not a number in field syntax, so it is a string key and str indexing raises TypeError. '{} {0}' raises with the message "cannot switch from manual field specification to automatic field numbering" — str.format reports the same mistake the other way round ("automatic … to manual"), because Formatter re-implements the numbering in Python. A misspelt attribute gets the usual "Did you mean" hint.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| format_string | str | yes | Text with {} replacement fields — the str.format syntax. Positional-only (3.7+), so a field may be called format_string. |
| *args | any | no | format(): positional values for {0}, {1} and {}. |
| **kwargs | any | no | format(): named values for {name}. |
| args, kwargs | sequence, mapping | no | vformat(format_string, args, kwargs): the same values as one sequence and one mapping — pass a dict you already have without ** unpacking. |
Return value
Formatter — A formatter object; call .format(format_string, *args, **kwargs) or .vformat(format_string, args, kwargs).
Common patterns
import string class Default(string.Formatter): def get_value(self, key, args, kwargs): if isinstance(key, str): return kwargs.get(key, "?") return super().get_value(key, args, kwargs) Default().format("{a}-{b}", a=1) # '1-?'
import string from collections import ChainMap f = string.Formatter() f.vformat("{user} at {host}", (), ChainMap(overrides, defaults))
import string class Upper(string.Formatter): def convert_field(self, value, conversion): if conversion == "u": return str(value).upper() return super().convert_field(value, conversion) Upper().format("{0!u}", "shout")
Examples
Pitfalls
import string d = {'a': 1} string.Formatter().vformat('{a}', **d)
import string d = {'a': 1} string.Formatter().vformat('{a}', (), d)
'{}{0.real}'.format(5)
'{0}{0.real}'.format(5)
import string class User: def __init__(self): self.name = 'ada' self._password = 'hunter2' string.Formatter().format('{u.name} {u._password}', u=User())
from string import Template Template('$name').safe_substitute(name='ada', password='hunter2')
When to use
- Changing how field names are resolved (defaults, dotted lookups in your own data, case-insensitive keys)
- Adding conversions (!u) or format-spec handling for your types without touching the types
- Validating format strings: forbid unused arguments, restrict field names
- Plain formatting → f-strings or str.format (faster, same result)
- Templates written by users → string.Template
Notes
FAQ
Building your own variant of str.format. You subclass it and override one hook — get_value to supply defaults, convert_field to add conversions, format_field to format values your way, check_unused_args to be strict — and keep the rest of the standard parsing and formatting.