Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make subclass use custom `__str__` in an f-string

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.

like image 333
Antoine Gallix Avatar asked Oct 21 '25 15:10

Antoine Gallix


1 Answers

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}"

like image 188
pixel-process Avatar answered Oct 23 '25 05:10

pixel-process



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!