I wrote a subclass of Decimal to represent an amount of money. I wrote a custom __str__ to display the currency along with a sign format. My method works when calling str() but in a f-string somehow my custom __str__ is not used. What is happening here?
My goal is to have my custom __str__ being used in f-string situations. I'm also interested in understanding what is happening here. Something is going on that defies my current understanding of __str__, __format__ and inheritance. I thought the default behavior of format with no format specified was to delegate to str, but here it sends it to str of the parent class instead.
Here is the minimal code to reproduce:
from decimal import Decimal
class Money(Decimal):
CURRENCY = "€"
def __new__(cls, number):
return super().__new__(cls, Decimal(number).quantize(Decimal("0.01")))
def __str__(self):
return f"{self:+}{self.CURRENCY}"
m = Money(10)
print("Test 1 - str():", str(m))
print("Test 2 - print():", m)
print(f"Test 3 - f-string: {m}")
print("Test 4 - f-string str():", f"{str(m)}")
This results in
Test 1 - str(): +10.00€
Test 2 - print(): +10.00€
Test 3 - f-string: 10.00 # only here is my custom str not called
Test 4 - f-string str(): +10.00€
Python version is 3.13.7.
f-strings in Python prioritize __format__ over __str__. Since Money inherits from Decimal, you need to add __format__ to your class for expected behavior.
from decimal import Decimal
class Money(Decimal):
CURRENCY = "€"
def __new__(cls, number):
return super().__new__(cls, Decimal(number).quantize(Decimal("0.01")))
def __format__(self, format_spec: str) -> str:
if not format_spec:
format_spec = "+.2f"
formatted_number = super().__format__(format_spec)
return f"{formatted_number}{self.CURRENCY}"
def __str__(self) -> str:
formatted_number = super().__format__("+.2f")
return f"{formatted_number}{self.CURRENCY}"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With