RecursionError when inheriting from float and using str and repr

Question:

I was testing some features in Python for fun 😉
But I have a recursion error that I don’t understand

class Test(float):
    def __new__(cls, value):
        return super().__new__(cls, value)

    def __str__(self):
        return super().__str__()
    
    def __repr__(self):
        return f'<value: {str(self)}>'


test = Test(12)
print(test)

Traceback:

Traceback (most recent call last):
  File "C:temptest_float.py", line 13, in <module>
    print(test)
  File "C:temptest_float.py", line 6, in __str__
    return super().__str__()
  File "C:temptest_float.py", line 9, in __repr__
    return f'<value: {str(self)}>'
  File "C:temptest_float.py", line 6, in __str__
    return super().__str__()
  File "C:temptest_float.py", line 9, in __repr__
    return f'<value: {str(self)}>'
...the above 2 errors repeated many times...
  File "C:temptest_float.py", line 6, in __str__
    return super().__str__()
RecursionError: maximum recursion depth exceeded

The line return super().__str__() should call float.__str__() and just returns ’12’.

Do you have any ideas ?

Asked By: tomtomfox

||

Answers:

Your __repr__ calls your __str__, which calls the super’s __str__, which defers to repr, which calls your __repr__, which is an infinite recursion. You could call super().__repr__ in your __repr__ method, instead of calling str(self).

class Test(float):
    def __new__(cls, value):
        return super().__new__(cls, value)

    def __str__(self):
        return super().__str__()
    
    def __repr__(self):
        return f'<value: {super().__repr__()}>'
>>> Test(12)
<value: 12.0>
Answered By: khelwood

The core issue is that float.__str__(self) will call self.__repr__() rather than float.__repr__(self).

Not only does that mean that you have an infinite recursion from Test.__repr__ to Test.__str__ to float.__str__ back to Test.__repr__, it means that Test.__str__ is going to print the same thing as Test.__repr__, which I assume you don’t want since you went to the effort of reimplementing it.

Instead I think you want:

class Test(float):
    def __str__(self):
        return super().__repr__()
    
    def __repr__(self):
        return f'<value: {super().__repr__()}>'
Answered By: Samwise