The magic methods __str__ and __repr__ are used for converting an instance object into a string. The method __str__ is invoked when an instance object is converted to a string by calling the str built-in function. It is also invoked when an instance object is printed using the print function because print implicitly calls the str built-in function. The method __repr__ is invoked by the repr built-in function, and it is also used to display the object in the interactive terminal. If __str__ is not defined, then this method is invoked for str(obj) and print(obj) also.
Both __str__ and __repr__ methods return a string representation of the instance, but __str__ is generally used for the end user of the class; it returns a human-readable and user-friendly string representation of the object. The string returned by __repr__ is a descriptive and unambiguous string representation of the object. It returns a Python-interpretable text that can be used by programmers for debugging. This text is generally a valid Python expression from which you can re-create the instance object using the eval function.
class Fraction: def __init__(self, nr, dr=1): self.nr = nr self.dr = dr if self.dr < 0: self.nr *= -1 self.dr *= -1 self._reduce() def show(self): print(f'{self.nr}/{self.dr}') def _reduce(self): h = Fraction.hcf(self.nr, self.dr) if h == 0: return self.nr //= h self.dr //= h @staticmethod def hcf(x, y): x = abs(x) y = abs(y) smaller = y if x > y else x s = smaller while s > 0: if x %s ==0 and y % s == 0: break s -= 1 return s def __eq__(self,other): return (self.nr * other.dr) == (self.dr * other.nr)
Let us see what happens when we print an instance object of our Fraction class.
>>> f = Fraction(2, 3)
>>> print(f)
<__main__.Fraction object at 0x000001C9DB23B100>
We get a string containing the class name and the object id. The interactive echo and str function will also give the same string.
>>> f
<__main__.Fraction object at 0x000001C9DB23B100>
'<__main__.Fraction object at 0x000001C9DB23B100>'
This is the way the interpreter prints an object by default. If we want to print the object in some other way then we have to change this default string representation by defining the __str__ and __repr__ methods.
If we want the output for the end user, we would like the data inside the object to be printed in some format. In our Fraction class, we had to call the show method to display a Fraction object in a user-friendly form. Now, let us define the __str__ method.
def __str__(self): return f'{self.nr}/{self.dr}'
This method returns the string that we were printing in the show method. This __str__ method will automatically be called by str and print functions.
>>> f = Fraction(2, 3)
>>> print(f)
2/3
>>str(f)
'2/3'
However, the interactive echo still gives the same output.
>>> f
'<__main__.Fraction object at 0x000001C9DB23B100>'
To change this, we have to define the __repr__ method.
def __repr__(self): return f'Fraction({self.nr},{self.dr})'
The string that is returned is the source code required to instantiate the object. That is why we have the initializer of the class.
>>> f
Fraction(2,3)
>>> repr(f)
'Fraction(2,3)'
This string, when passed to the eval function, will create an equivalent object, and so, for most objects, eval(repr(obj)) == obj will be True provided we have an appropriate __eq__ method defined.
>>> x = eval(repr(f))
>>> print(x)
2/3
>>> eval(repr(f)) == f
True
zzh@ZZHPC:/zdata/Github/python$ python Python 3.12.3 (main, May 16 2024, 09:18:37) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import aaa >>> f = aaa.Fraction(2, 3) >>> print(f) 2/3 >>> str(f) '2/3' >>> f <aaa.Fraction object at 0x76587b535b50> >>> from importlib import reload >>> reload(aaa) <module 'aaa' from '/zdata/Github/python/aaa.py'> >>> f <aaa.Fraction object at 0x76587b535b50> >>> f = aaa.Fraction(2, 3) >>> f Fraction(2,3) >>> repr(f) 'Fraction(2,3)' >>> x = eval(repr(f)) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> NameError: name 'Fraction' is not defined >>> from aaa import * >>> f = Fraction(2, 3) >>> f Fraction(2,3) >>> repr(f) 'Fraction(2,3)' >>> x = eval(repr(f)) >>> print(x) 2/3 >>> eval(repr(f)) == f True