Python format Method: Custom String Formatting
In Python, custom string representations are typically handled by
__str__ and __repr__, but context-dependent
and customized string formatting relies on the __format__
magic method. This article explains the role of __format__,
how it interacts with Python's f-strings and the built-in
format() function, how it differs from other
string-conversion methods, and how to implement it in your own classes
to parse custom format specifiers.
What is the
__format__ Method?
The __format__ method is a special method in Python
invoked whenever an object is evaluated within a formatting context.
This includes calls to the built-in
format(value, format_spec) function, the
str.format() method, and formatted string literals
(f-strings).
Its signature is:
def __format__(self, format_spec: str) -> str:
...The method receives a single argument, format_spec,
which is a string containing formatting instructions defined after the
colon : in a format expression (for example,
f"{value:format_spec}"). The method must return a
string.
How
__format__ Differs from __str__ and
__repr__
__repr__: Aimed at developers; provides an unambiguous, detailed representation of an object, often matching the code needed to recreate it.__str__: Aimed at end users; returns a single, readable, default representation of an object.__format__: Designed for dynamic presentation; accepts format specifiers to change how an object is displayed depending on the context (e.g., date formats, alignment, or numeric precision).
If a class does not define __format__, Python falls back
to object.__format__. If an empty format_spec
is provided, object.__format__ calls __str__.
However, if a non-empty format_spec is passed to an object
that relies on default inheritance, Python raises a
TypeError.
Implementing
__format__
To implement custom formatting, evaluate the incoming
format_spec string and return the appropriately styled
output.
Example: Custom Currency Formatter
class Money:
def __init__(self, amount: float):
self.amount = amount
def __format__(self, format_spec: str) -> str:
if format_spec == "USD":
return f"${self.amount:,.2f}"
elif format_spec == "EUR":
return f"€{self.amount:,.2f}"
elif format_spec == "GBP":
return f"£{self.amount:,.2f}"
elif format_spec == "raw":
return str(self.amount)
# Fall back to standard float formatting if no currency match
return format(self.amount, format_spec)Usage:
price = Money(1250.5)
# Using f-strings
print(f"{price:USD}") # Output: $1,250.50
print(f"{price:EUR}") # Output: €1,250.50
print(f"{price:raw}") # Output: 1250.5
# Delegating to standard numeric specifications
print(f"{price:.0f}") # Output: 1250
# Using format() directly
print(format(price, "GBP")) # Output: £1,250.50Best Practices
- Handle Empty Format Specifications: Always support
an empty string
""asformat_spec. The standard convention is to delegate an empty specifier to__str__()or provide the natural default formatting. - Delegate Standard Types: If your object wraps a
standard type (like an
int,float, ordatetime), pass unrecognized specifiers directly to that attribute'sformat()function rather than raising an error immediately. - Raise
ValueErrorfor Invalid Codes: If a user provides an unsupported or malformed format specifier, raise aValueErrorwith a descriptive message to match the behavior of built-in Python types.