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.

string classPython 2.6+ (format string positional-only since 3.7)Live demo
Common call
string.Formatter().format('{0} {name}', 'hi', name='Ada')
Returns
'hi Ada' — a str
Replaces
regex-based mini template engines that re-implement {field} lookup
Watch out
vformat takes args and kwargs as two objects, not * and ** unpacked
string.Formatter()
→ Formatter

Demo

Live evaluation
Positional values from a comma-separated list, plus one keyword, name.
Try:
Inputs
fmtstrformat string
argslist[str]positional values, a,b,c
namestrvalue for {name}
Code
import string
string.Formatter().format('{} and {} by {name}', *['tea', 'cake'], name='Ada')
Result
'tea and cake by 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

NameTypeRequiredDescription
format_stringstryesText with {} replacement fields — the str.format syntax. Positional-only (3.7+), so a field may be called format_string.
*argsanynoformat(): positional values for {0}, {1} and {}.
**kwargsanynoformat(): named values for {name}.
args, kwargssequence, mappingnovformat(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

Default for missing keys
Override get_value; positional keys still go to the base class.
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-?'
Format from a dict without **
vformat takes the mapping directly — any Mapping works, including ChainMap.
import string
from collections import ChainMap
f = string.Formatter()
f.vformat("{user} at {host}", (), ChainMap(overrides, defaults))
Custom conversion flag
convert_field receives the character after !; add your own.
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

1. Same result as str.format
import string string.Formatter().format('{} + {} = {total}', 2, 3, total=5)
Returns
'2 + 3 = 5'
2. vformat with a dict
import string string.Formatter().vformat('{greeting}, {0}!', ('World',), {'greeting': 'Hello'})
Returns
'Hello, World!'
3. Format spec and conversion
import string string.Formatter().format('{0:>6}|{0!r}', 'ab')
Returns
" ab|'ab'"
4. Index and attribute access
import string string.Formatter().format('{0[1]} {1.imag}', 'xyz', 3j)
Returns
'y 3.0'
5. Missing keyword
import string string.Formatter().format('{x}', y=1)
Returns
KeyError: 'x'
6. Missing position
import string string.Formatter().format('{2}', 'a')
Returns
IndexError: tuple index out of range
7. Only one nested level
import string string.Formatter().format('{0:{1:{2}}}', 'a', '5', 'c')
Returns
ValueError: Max string recursion exceeded

Pitfalls

1. Unpacking into vformat
vformat takes exactly three arguments: the string, a sequence and a mapping.
vformat(s, **d)
import string
d = {'a': 1}
string.Formatter().vformat('{a}', **d)
TypeError: Formatter.vformat() got an unexpected keyword argument 'a'
vformat(s, (), d)
import string
d = {'a': 1}
string.Formatter().vformat('{a}', (), d)
'1'
2. Assuming str.format and Formatter agree on every edge
Formatter re-implements automatic numbering in Python and only treats an all-digit field as manual. "{}{0.real}" is an error for str.format but accepted by Formatter.
str.format
'{}{0.real}'.format(5)
ValueError: cannot switch from automatic field numbering to manual field specification
pick one style
'{0}{0.real}'.format(5)
'55'
3. Formatting untrusted format strings
Field names can walk attributes and indexes of the values you pass, so a user-supplied format string can read data you did not mean to expose.
attribute walk
import string
class User:
    def __init__(self):
        self.name = 'ada'
        self._password = 'hunter2'
string.Formatter().format('{u.name} {u._password}', u=User())
'ada hunter2'
string.Template
from string import Template
Template('$name').safe_substitute(name='ada', password='hunter2')
'ada'

When to use

Use it
  • 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
Reach for something else
  • Plain formatting → f-strings or str.format (faster, same result)
  • Templates written by users → string.Template

Notes

CPython impl
Lib/string.py, pure Python: vformat → _vformat loops over self.parse(), resolves with get_field, then convert_field and format_field; parsing itself is the C parser of str.format (_string.formatter_parser)
Recursion
Every format spec is formatted again with recursion_depth - 1, starting from 2 — so one level of nested fields such as {0:{1}} works and a second level raises ValueError
used_args
vformat collects the keys actually used and passes them to check_unused_args (which does nothing by default)

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.