String formatting specification#
The conversion of Unit, Quantity and Measurement
objects to strings (e.g. through the str builtin or f-strings) can be
customized using format specifications. The basic format is:
[magnitude format][modifier][pint format]
where each part is optional and the order of these is arbitrary.
>>> import pint
>>> ureg = pint.UnitRegistry()
>>> q = 2.3e-6 * ureg.m ** 3 / (ureg.s ** 2 * ureg.kg)
>>> f"{q:~P}" # short pretty
'2.3×10⁻⁶ m³/kg/s²'
>>> f"{q:~^P}" # short pretty with negative exponents
'2.3×10⁻⁶ kg⁻¹⋅m³⋅s⁻²'
>>> f"{q:~#P}" # compact short pretty
'2.3 mm³/g/s²'
>>> f"{q:P#~}" # also compact short pretty
'2.3 mm³/g/s²'
>>> f"{q:.2f~#P}" # short compact pretty with 2 float digits
'2.30 mm³/g/s²'
>>> f"{q:#~}" # short compact default
'2.3 mm ** 3 / g / s ** 2'
In case the format is omitted, the corresponding value in the formatter
.default_format attribute is filled in. For example:
>>> ureg.formatter.default_format = "P"
>>> f"{q}"
'2.3×10⁻⁶ meter³/kilogram/second²'
Pint Format Types#
pint comes with a variety of unit formats. These impact the complete representation:
Spec |
Name |
Examples |
|---|---|---|
|
default |
|
|
pretty |
|
|
HTML |
|
|
latex |
|
|
latex siunitx |
|
|
compact |
|
These examples are using g` as numeric modifier. Measurement are also affected
by these modifiers.
Quantity modifiers#
Modifier |
Meaning |
Example |
|---|---|---|
|
Call |
|
Unit modifiers#
Modifier |
Meaning |
Example |
|---|---|---|
|
Use the unit’s symbol instead of its canonical name |
|
|
Use negative exponents instead of ratio |
|
|
Show exponent as a fraction |
|
Note
The / modifier converts floats to the nearest fraction with a
denominator of at most 1000, hiding floating point issues:
>>> f"{ureg.Unit('second') ** -0.9999999999999998:~/P}"
'1/s'
>>> f"{ureg.Unit('meter') ** 5.55e-17:~/P}"
'm⁰'
Magnitude modifiers#
Pint uses the format specifications. However, it is important to remember that only the type honors the locale. Using any other numeric format (e.g. g, e, f) will result in a non-localized representation of the number.
Ordering units#
When a quantity has a compound unit (e.g. after multiplying two quantities
together), the order in which the individual units appear is controlled by
ureg.formatter.default_sort_func. By default, units are sorted alphabetically
by name (pint.delegates.formatter.sort_by_unit_name()).
A sort function must have the following signature:
def sort_func(
items: Iterable[tuple[str, Any, str]],
registry: UnitRegistry | None,
) -> Iterable[tuple[str, Any, str]]: ...
where items is an iterable of (display_name, exponent, unit_name)
triplets, one per unit in the compound unit, and registry is the
UnitRegistry in use (or None). It must return the same
triplets in the desired display order. This is exposed as the
pint.delegates.formatter.SortFunc type alias.
To instead sort by dimensionality, use
pint.delegates.formatter.sort_by_dimensionality(),
which orders units according to ureg.formatter.dim_order:
>>> ureg2 = pint.UnitRegistry()
>>> ureg2.formatter.dim_order
('[substance]', '[mass]', '[current]', '[luminosity]', '[length]', '[]', '[time]', '[temperature]')
>>> from pint.delegates.formatter import sort_by_dimensionality
>>> ureg2.formatter.default_sort_func = sort_by_dimensionality
>>> Q2_ = ureg2.Quantity
>>> str(Q2_(1, "m") * Q2_(1, "N"))
'1 newton * meter'
Reordering dim_order changes which dimension’s unit is listed first:
>>> ureg2.formatter.dim_order = ('[temperature]', '[time]', '[]', '[length]', '[luminosity]', '[current]', '[mass]', '[substance]')
>>> str(Q2_(1, "m") * Q2_(1, "N"))
'1 meter * newton'
For full control (e.g. ordering by a specific list of preferred units rather
than by dimension), write a custom sort function matching the signature above
and assign it to default_sort_func.
Custom formats#
Using pint.register_unit_format(), it is possible to add custom
formats:
>>> @pint.register_unit_format("Z")
... def format_unit_simple(unit, registry, **options):
... return " * ".join(f"{u} ** {p}" for u, p in unit.items())
>>> f"{q:Z}"
'2.3e-06 kilogram ** -1 * meter ** 3 * second ** -2'
where unit is a dict subclass containing the unit names and
their exponents, registry is the current instance of :py:class:UnitRegistry and
options is not yet implemented.
You can choose to replace the complete formatter. Briefly, the formatter if an object with the following methods: format_magnitude, format_unit, format_quantity, format_uncertainty, format_measurement. The easiest way to create your own formatter is to subclass one that you like and replace the methods you need. For example, to replace the unit formatting:
>>> from pint.delegates.formatter.plain import DefaultFormatter
>>> class MyFormatter(DefaultFormatter):
...
... default_format = ""
...
... def format_unit(self, unit, uspec, sort_func, **babel_kwds) -> str:
... return "ups!"
...
>>> ureg.formatter = MyFormatter()
>>> ureg.formatter._registry = ureg
>>> str(q)
'2.3e-06 ups!'
By replacing other methods, you can customize the output as much as you need.
SciForm is a library that can be used to format the magnitude of the number. This can be used in a customer formatter as follows:
>>> from sciform import Formatter
>>> sciform_formatter = Formatter(round_mode="sig_fig", ndigits=4, exp_mode="engineering")
>>> class MyFormatter(DefaultFormatter):
...
... default_format = ""
...
... def format_magnitude(self, value, spec, **options) -> str:
... return sciform_formatter(value)
...
>>> ureg.formatter = MyFormatter()
>>> ureg.formatter._registry = ureg
>>> str(q * 10)
'23.00e-06 meter ** 3 / second ** 2 / kilogram'