Search code examples
pythonenumsf-string

f-string representation different than str()


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?


Solution

  • 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!'"