Bool is converted to int in an f-string, is this supposed to happen?

Not sure if this is expected behavior or could be considered a bug, just something I noticed this morning.

Python 3.12.4 | packaged by conda-forge | (main, Jun 17 2024, 10:23:07) [GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = True
>>> f"{a}"
'True'  # as expected
>>> f"{a:5}"
'    1'  # huh?
>>> f"{a!s:5}"
'True '   # that's what I wanted!
>>> 

It was surprising to me that, when I started to add formatting to my f-string, it converted the bool to an int. Clearly I can get around it for now, but I wasn’t sure if this behavior is expected.

I guess the root cause here is that bool doesn’t have a __format__ method and so it uses the superclass (int) method:

>>> type(a)
<class 'bool'>
>>> type(a).__format__
<method '__format__' of 'int' objects>
4 Likes
>>> isinstance(True, int)
True

bool inherits from int and it’s probably reverting to that in the formatting. TBH, I’m not sure how much of a use case there is for formatting a bool so this might’ve been overlooked unintentionally.

It’s “intended” in the sense that this is the obvious result of the way bool is defined. I suspect that your suspected behavior of behaving more like str by default would be useful, but I fear that it would break a surprising amount of code. But that would have to be checked, and I have no idea how to do that.

1 Like

I think the surprising thing here is that int.__format__[1] has a special-case to be equivalent to str() when there’s no format string. So even though bool use the int formatting code, it ends up calling PyObject_Str.

Like you, I have no idea if anyone relies on this behavior, but presumably someone does somewhere.


  1. more specifically, _PyLong_FormatAdvancedWriter ↩︎

This is not surprising IMO, this is more or less required by the contract for __format__. It should always delicate to __str__ if the format string is empty, for all classes. Note that if this weren’t the case, subclasses of int, str, float, … would never be correctly displayed by either of the "{}" syntax options unless explicitly using !s.

Well not never: only if their string representation is different from the superclass, as in bool. :wink: If I subclass int to do something weird but I still want it to display normally, I’d let the normal formatter handle everything.

So the specific thing I find surprising here[1] is that booleans have their own __str__ but not their own __format__.


  1. your own level of surprise may vary ↩︎

1 Like