Purpose of _Repr_ Method

What is the difference between __str__ and __repr__?

Alex summarized well but, surprisingly, was too succinct.

First, let me reiterate the main points in Alex’s post:

  • The default implementation is useless (it’s hard to think of one which wouldn’t be, but yeah)
  • __repr__ goal is to be unambiguous
  • __str__ goal is to be readable
  • Container’s __str__ uses contained objects’ __repr__

Default implementation is useless

This is mostly a surprise because Python’s defaults tend to be fairly useful. However, in this case, having a default for __repr__ which would act like:

return "%s(%r)" % (self.__class__, self.__dict__)

would have been too dangerous (for example, too easy to get into infinite recursion if objects reference each other). So Python cops out. Note that there is one default which is true: if __repr__ is defined, and __str__ is not, the object will behave as though __str__=__repr__.

This means, in simple terms: almost every object you implement should have a functional __repr__ that’s usable for understanding the object. Implementing __str__ is optional: do that if you need a “pretty print” functionality (for example, used by a report generator).

The goal of __repr__ is to be unambiguous

Let me come right out and say it — I do not believe in debuggers. I don’t really know how to use any debugger, and have never used one seriously. Furthermore, I believe that the big fault in debuggers is their basic nature — most failures I debug happened a long long time ago, in a galaxy far far away. This means that I do believe, with religious fervor, in logging. Logging is the lifeblood of any decent fire-and-forget server system. Python makes it easy to log: with maybe some project specific wrappers, all you need is a

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

But you have to do the last step — make sure every object you implement has a useful repr, so code like that can just work. This is why the “eval” thing comes up: if you have enough information so eval(repr(c))==c, that means you know everything there is to know about c. If that’s easy enough, at least in a fuzzy way, do it. If not, make sure you have enough information about c anyway. I usually use an eval-like format: "MyClass(this=%r,that=%r)" % (self.this,self.that). It does not mean that you can actually construct MyClass, or that those are the right constructor arguments — but it is a useful form to express “this is everything you need to know about this instance”.

Note: I used %r above, not %s. You always want to use repr() [or %r formatting character, equivalently] inside __repr__ implementation, or you’re defeating the goal of repr. You want to be able to differentiate MyClass(3) and MyClass("3").

The goal of __str__ is to be readable

Specifically, it is not intended to be unambiguous — notice that str(3)==str("3"). Likewise, if you implement an IP abstraction, having the str of it look like 192.168.1.1 is just fine. When implementing a date/time abstraction, the str can be "2010/4/12 15:35:22", etc. The goal is to represent it in a way that a user, not a programmer, would want to read it. Chop off useless digits, pretend to be some other class — as long is it supports readability, it is an improvement.

Container’s __str__ uses contained objects’ __repr__

This seems surprising, doesn’t it? It is a little, but how readable would it be if it used their __str__?

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

Not very. Specifically, the strings in a container would find it way too easy to disturb its string representation. In the face of ambiguity, remember, Python resists the temptation to guess. If you want the above behavior when you’re printing a list, just

print("[" + ", ".join(l) + "]")

(you can probably also figure out what to do about dictionaries.

Summary

Implement __repr__ for any class you implement. This should be second nature. Implement __str__ if you think it would be useful to have a string version which errs on the side of readability.

Purpose of __repr__ method?

__repr__ should return a printable representation of the object, most likely one of the ways possible to create this object. See official documentation here. __repr__ is more for developers while __str__ is for end users.

A simple example:

>>> class Point:
... def __init__(self, x, y):
... self.x, self.y = x, y
... def __repr__(self):
... cls = self.__class__.__name__
... return f'{cls}(x={self.x!r}, y={self.y!r})'
>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)

what is the significance of `__repr__` function over normal function

The __repr__ function is called by repr() internally. repr() is called when you are printing the object directly , and the class does not define a __str__() . From documentation -

object.__repr__(self)

Called by the repr() built-in function and by string conversions (reverse quotes) to compute the “official” string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form <...some useful description...> should be returned. The return value must be a string object. If a class defines __repr__() but not __str__(), then __repr__() is also used when an “informal” string representation of instances of that class is required.

In your case for print_class() , you have to specifically call the method when printing the object. But in case of __repr__() , it gets internally called by print .

This is especially useful, when you are mixing different classes/types . For Example lets take a list which can have numbers and objects of your point class, now you want to print the elements of the list.

If you do not define the __repr__() or __str__() , you would have to first check the instance , whether its of type Point if so call print_class() , or if not directly print the number.

But when your class defines the __repr__() or __str__() , you can just directly call print on all the elements of the list, print statement would internally take care of printing the correct values.

Example , Lets assume a class which has print_class() method, but no __repr__() or __str__() , code -

>>> class CA:
... def __init__(self,x):
... self.x = x
... def print_class(self):
... return self.x
...
>>> l = [1,2,3,CA(4),CA(5)]
>>> for i in l:
... print(i)
...
1
2
3
<__main__.CA object at 0x00590F10>
<__main__.CA object at 0x005A5070>
SyntaxError: invalid syntax
>>> for i in l:
... if isinstance(i, CA):
... print(i.print_class())
... else:
... print(i)
...
1
2
3
4
5

As you can see, when we mix numbers and objects of type CA in the list, and then when we just did print(i) , it did not print what we wanted. For this to work correctly, we had to check the type of i and call the appropriate method (as done in second case).

Now lets assume a class that implements __repr__() instead of print_class() -

>>> class CA:
... def __init__(self,x):
... self.x = x
... def __repr__(self):
... return str(self.x)
...
>>>
>>> l = [1,2,3,CA(4),CA(5)]
>>> for i in l:
... print(i)
...
1
2
3
4
5

As you can see in second case, simply printing worked, since print internally calls __str__() first, and as that did not exist fell back to __repr__() .

And not just this, when we do str(list) , internally each list's element's __repr__() is called. Example -

First case (without __repr__() ) -

>>> str(l)
'[1, 2, 3, <__main__.CA object at 0x005AB3D0>, <__main__.CA object at 0x005AB410>]'

Second case (with __repr__() ) -

>>> str(l)
'[1, 2, 3, 4, 5]'

Also, in interactive interpreter, when you are directly using the object, it shows you the output of repr() function, Example -

>>> class CA:
... def __repr__(self):
... return "CA instance"
...
>>>
>>> c = CA()
>>> c
CA instance

What are the best practices for __repr__ with collection class Python?

The official documentation outlines this as how you should handle __repr__:

Called by the repr() built-in function to compute the “official”
string representation of an object. If at all possible, this should
look like a valid Python expression that could be used to recreate an
object with the same value (given an appropriate environment). If this
is not possible, a string of the form <...some useful description...>
should be returned. The return value must be a string object. If a
class defines __repr__() but not __str__(), then __repr__() is also
used when an “informal” string representation of instances of that
class is required.

This is typically used for debugging, so it is important that the
representation is information-rich and unambiguous.

https://docs.python.org/3/reference/datamodel.html#object.\_\_repr__

Lists, strings, sets, tuples and dictionaries all print out the entirety of their collection in their __repr__ method.

Your current code looks to perfectly follow the example of what the documentation suggests. Though I would suggest changing your __init__ method so it looks more like this:

class MyCollection:
def __init__(self, objects=None):
if objects is None:
objects = []
self._objects = objects

def __repr__(self):
return f"MyCollection({self._objects})"

You generally want to avoid using mutable objects as default arguments. Technically because of the way your method is implemented using extend (which makes a copy of the list), it will still work perfectly fine, but Python's documentation still suggests you avoid this.

It is good programming practice to not use mutable objects as default
values. Instead, use None as the default value and inside the
function, check if the parameter is None and create a new
list/dictionary/whatever if it is.

https://docs.python.org/3/faq/programming.html#why-are-default-values-shared-between-objects

If you're interested in how another library handles it differently, the repr for Numpy arrays only shows the first three items and the last three items when the array length is greater than 1,000. It also formats the items so they all use the same amount of space (In the example below, 1000 takes up four spaces so 0 has to be padded with three more spaces to match).

>>> repr(np.array([i for i in range(1001)]))
'array([ 0, 1, 2, ..., 998, 999, 1000])'

To mimic this numpy array style you could implement a __repr__ method like this in your class:

class MyCollection:
def __init__(self, objects=None):
if objects is None:
objects = []
self._objects = objects

def __repr__(self):
# If length is less than 1,000 return the full list.
if len(self._objects) < 1000:
return f"MyCollection({self._objects})"
else:
# Get the first and last three items
items_to_display = self._objects[:3] + self._objects[-3:]
# Find the which item has the longest repr
max_length_repr = max(items_to_display, key=lambda x: len(repr(x)))
# Get the length of the item with the longest repr
padding = len(repr(max_length_repr))
# Create a list of the reprs of each item and apply the padding
values = [repr(item).rjust(padding) for item in items_to_display]
# Insert the '...' inbetween the 3rd and 4th item
values.insert(3, '...')
# Convert the list to a string joined by commas
array_as_string = ', '.join(values)
return f"MyCollection([{array_as_string}])"

>>> repr(MyCollection([1,2,3,4]))
'MyCollection([1, 2, 3, 4])'

>>> repr(MyCollection([i for i in range(1001)]))
'MyCollection([ 0, 1, 2, ..., 998, 999, 1000])'

What is the purpose of __str__ and __repr__?

__repr__

Called by the repr() built-in function and by string conversions (reverse quotes) to compute the "official" string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment).

__str__

Called by the str() built-in function and by the print statement to compute the "informal" string representation of an object.

Use __str__ if you have a class, and you'll want an informative/informal output, whenever you use this object as part of string. E.g. you can define __str__ methods for Django models, which then gets rendered in the Django administration interface. Instead of something like <Model object> you'll get like first and last name of a person, the name and date of an event, etc.


__repr__ and __str__ are similar, in fact sometimes equal (Example from BaseSet class in sets.py from the standard library):

def __repr__(self):
"""Return string representation of a set.

This looks like 'Set([<list of elements>])'.
"""
return self._repr()

# __str__ is the same as __repr__
__str__ = __repr__

What is the purpose of thing sub-function returning __repr__ with f str

The repr method is used to get a string representation of a Python object. It is common to find people using it when creating models for their flask app.

With the repr method, you can make a query from the database and print the result of the query. Instead of getting the location of the query object in memory, the repr method provides a better representation of the result.

Using your example:

class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(120), nullable=False)
username = db.Column(db.String(120), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(120), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
#posts = db.relationship('Aaa', backref='author', lazy=True)

def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"

If you have a user with the following details:

  • id = 1
  • first_name = 'Billy'
  • username = 'Bly'
  • email = 'Billyjoe@bj.com'
  • image_file = 'default.jpg'
  • password = 'sample'

On printing the result from the query, you will get:

"User('Bly', 'Billyjoe@bj.com', 'default.jpg')"

The result above is better than something that looks like:

<__main__.User object at 0x7f6882314da0> 

How to set a repr for a function itself?

I think a custom decorator could help:

import functools

class reprable:
"""Decorates a function with a repr method.

Example:
>>> @reprable
... def foo():
... '''Does something cool.'''
... return 4
...
>>> foo()
4
>>> foo.__name__
'foo'
>>> foo.__doc__
'Does something cool.'
>>> repr(foo)
'foo: Does something cool.'
>>> type(foo)
<class '__main__.reprable'>
"""

def __init__(self, wrapped):
self._wrapped = wrapped
functools.update_wrapper(self, wrapped)

def __call__(self, *args, **kwargs):
return self._wrapped(*args, **kwargs)

def __repr__(self):
return f'{self._wrapped.__name__}: {self._wrapped.__doc__}'

Demo: http://tpcg.io/uTbSDepz.



Related Topics



Leave a reply



Submit