70

I had always thought that f-strings invoked the __str__ method. That is, f'{x}' was always the same as str(x). However, with this class

class Thing(enum.IntEnum):
    A = 0

f'{Thing.A}' is '0' while str(Thing.A) is 'Thing.A'. This example doesn't work if I use enum.Enum as the base class.

What functionality do f-strings invoke?

Daniel Walker
  • 6,380
  • 5
  • 22
  • 45
  • 24
    In Python3.11 the `str()` output has been changed to match the `.format()` method for `IntEnum`, `IntFlag`, and the new `StrEnum` -- so in the case above both `f'{Thing.A}'` and `str(Thing.A)` would result in `0`. – Ethan Furman May 31 '22 at 17:08

2 Answers2

98

From "Formatted string literals" in the Python reference: f-strings invoke the "format() protocol", meaning that the __format__ magic method is called instead of __str__.

class Foo:
    def __repr__(self):
        return "Foo()"

    def __str__(self):
        return "A wild Foo"
    
    def __format__(self, format_spec):
        if not format_spec:
            return "A formatted Foo"
        return f"A formatted Foo, but also {format_spec}!"

>>> foo = Foo()
>>> repr(foo)
'Foo()'
>>> str(foo)
'A wild Foo'
>>> format(foo)
'A formatted Foo'
>>> f"{foo}"
'A formatted Foo'
>>> format(foo, "Bar")
'A formatted Foo, but also Bar!'
>>> f"{foo:Bar}"
'A formatted Foo, but also Bar!'

If you don't want __format__ to be called, you can specify !s (for str), !r (for repr) or !a (for ascii) after the expression:

>>> foo = Foo()
>>> f"{foo}"
'A formatted Foo'
>>> f"{foo!s}"
'A wild Foo'
>>> f"{foo!r}"
'Foo()'

This is occasionally useful with strings:

>>> key = 'something\n nasty!'
>>> error_message = f"Key not found: {key!r}"
>>> error_message
"Key not found: 'something\\n nasty!'"
wjandrea
  • 28,235
  • 9
  • 60
  • 81
decorator-factory
  • 2,733
  • 13
  • 25
39

f-strings in Python don't use __str__ or __repr__. They use __format__. So to get the same result as f'{Thing.A}', you'd need to call format(Thing.A).

The __format__(...) method allows you to add more formatting features (for example with floats you can do {:.2f} to round the number to two decimals).

If format() hasn't been defined for a class/object, python will fall back to __str__. That's why most people think str() is the method used in f-strings.

The docs cover the options you have with __format__ in detail: Link to Documentation

Tim Woocker
  • 1,883
  • 1
  • 16
  • 29