What Are The Differences Between __str__ and __repr__ In Class Methods?

Hello folks,

I am currently studying Python and currently on the topic of classes and overloading.
I have seen __str__ and __repr__ used interchangeably. What are the main differences
and what are the pros/cons of using one over the other? There was a test class in the book and I interchanged them myself and observed no noticeable differences in the output.

So, as they say, …, what’s the big idea?

Does this help python - What is the difference between __str__ and __repr__? - Stack Overflow

I was actually reading that a few minutes ago. However, once I started reading the comments below, there appears to be disagreement. So, I was hoping to get a response where there is less reason for ambiguity.

Define __repr__ to return what you need to see about your class in debug messages.

Define __str__ to return what is useful when asked to show to humans, if meaningful for the class. Not all classes have such a need.
The default is to use the repr() if not defined.

3 Likes

Ignore the comments, the answer itself is fully clear (except the part about debugging, just ignore it). The bests answers this thread here could generate are just copies of that SO answer.

1 Like

Thank you.

I have a related question. Here is the book class example:

class Person:

    def __init__(self, name, job = None, pay = 0):
        self.name = name
        self.job = job
        self.pay = pay

    def lastName(self):        
        return self.name.split()[-1]    

    def giveRaise(self, percent):
        self.pay = float(self.pay * (1 + percent))

    def __repr__(self): # or __str__
        return '[Person: %s, %s]' % (self.name, self.pay)

# Code not automatically run when imported into another module  
if __name__ == '__main__':

    sue = Person('Sue Jones', job = 'dev', pay = 100000)
    print(sue.name, sue.pay)
   
    sue.giveRaise(.10)                    # instead of hardcoding
    print('$%0.2f'% sue.pay)

    print(sue)

I know how to limit numbers to the nth decimal place. For example, for two decimal places:

test_num = 45.12568
print('The value is: %0.2f' % test_num)     # or
print('The value is: {0:02f}'.format(test_num))

But with __repr__ method above, how would you accomplish this being that it is returning a string? For print(sue), on my system, the output is:

[Person: Sue Jones, 110000.00000000001]

You wouldn’t, that isn’t the point of repr. Infact, [Person: %s, %s] is a bad repr, for such a class that just stores a few values the repr should be Person(name=%r, job=%r, pay=%r). The goal is to preserve most information, so rounding is not something that should be done there.

__str__ can just choose a reasonable default, for example two decimal places, for example with [Person: %s, %0.2f]. If the user of the class wants more control, they can manually access the attribute and display it. If the programmer implementing the Person class wants to have more complex support, they can implement the __format__ magic method.

1 Like

Ok, got it!

Yes, changing the %s to %0.2f did the trick.

Much obliged.

Also, the Python docs on the data model are a good read for __repr__, __str__, and more: 3. Data model — Python 3.12.1 documentation

First, I wouldn’t recommend using [] around your __repr__ because that will make users think it’s a list.

If you don’t want to change the __repr__, or anything else in the class, you are out of luck (except by parsing the string you get back, which is going to be really messy).

if you want to change how the repr is formatted for everyone, just change the logic in __repr__ accordingly. Of course, by having a str and a repr you offer two hard-coded options. (You should still follow convention: __str__, implementing str, is for making it look good; __repr__, implementing repr, is for making it accurately represent the object’s state.)

If you want to allow for outside code to have formatting options, then you need to implement __format__, and you also need to use modern formatting in the outside code (either f-strings or the format method, not % style formatting). Here’s a simple example that just forwards the formatting arguments to format self.pay, and leaves the self.name part as an ordinary string (and also shows using an f-string to implement it):

class Person:
    # the rest as before
    def __format__(self, fmt):
        return f'<Person: {self.name}, {self.pay:{fmt}}>'

(Hmm, the syntax colouring is a bit bugged…)

1 Like

That is actually how it was written in the book (but I understand your point).

For the sample test code that you provided, and based on the example code from the book above, what output did you expect? I ran the code along with the current test code, I see no upside. That is precisely the output one gets (observes) without the overloading methods (and the reason for implementing the overloading methods in the first place).

Here’s how we can use it. I’ll fill in a bit of detail so the example is stand-alone, but still ignoring the rest of the functionality of the class:

class Person:
    def __init__(self, name, pay):
        self.name, self.pay = name, pay
    def __format__(self, fmt):
        return f'<Person: {self.name}, {self.pay:{fmt}}>'

If we have that, and create an instance like:

worker = Person('Monty', 100.0)

Then it gets used by f-string or .format formatting:

>>> f'{worker}'
'<Person: Monty, 100.0>'

And we can do fun things like:

>>> f'{worker:>010.2f}'
'<Person: Monty, 0000100.00>'

This does not get used by __str__ or __repr__ by default:

>>> str(worker)
'<__main__.Person object at 0x...>'
>>> repr(worker)
'<__main__.Person object at 0x...>'

But we could make __str__ use it explicitly:

class Person:
    def __init__(self, name, pay):
        self.name, self.pay = name, pay
    def __format__(self, fmt):
        return f'<Person: {self.name}, {self.pay:{fmt}}>'
    def __str__(self):
        return self.__format__('')

Hi,

this part does not take care of the decimal places.

The previous version does:

return 'Person(name = %r, job = %r, pay = $%0.2f)' % (self.name, self.job, self.pay)

Did you try passing an appropriate format value? Where the example says return self.__format__(''), that is deliberately forwarding from __str__ to __format__, specifying the default format for the self.pay value. If you tried the rest of the examples, it should be clear what to pass instead.

This appears to be an implementation of either __str__ or __repr__, not of __format__. I also don’t see it anywhere previously in the thread, so I can’t tell what you mean by “the previous version”.

Hi, my apologies. I had assumed you were following along. From Cornelius’ response, he had already provided a workaround. I had incorporated his recommendation into the __repr__ overloading function.

Ah. Well, it can be just as easily incorporated into the __format__ approach (left as an exercise) :wink: