Implementing the __repr__
Method
In addition to __str__
, Python provides another special method for string representation: __repr__
. While __str__
is meant to provide a human-readable representation, __repr__
is intended to provide an unambiguous representation of an object that can be used to recreate the object if possible.
Understanding the __repr__
Method
The __repr__
method is called when you use the repr()
function on an object or when you display an object in an interactive session. It should return a string that, when passed to eval()
, would create an equivalent object (when possible).
Let's update our Book
class to include a __repr__
method:
-
Open book.py
in the editor.
-
Update the code to include a __repr__
method:
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def __str__(self):
return f'"{self.title}" by {self.author} ({self.pages} pages)'
def __repr__(self):
return f'Book("{self.title}", "{self.author}", {self.pages})'
## Create book objects
book1 = Book("The Great Gatsby", "F. Scott Fitzgerald", 180)
book2 = Book("To Kill a Mockingbird", "Harper Lee", 281)
## Print the books (uses __str__)
print("String representation (using __str__):")
print(book1)
print(book2)
## Get the representation (uses __repr__)
print("\nRepresentation (using __repr__):")
print(repr(book1))
print(repr(book2))
-
Save the file.
-
Run the script:
python3 book.py
You should see output like:
String representation (using __str__):
"The Great Gatsby" by F. Scott Fitzgerald (180 pages)
"To Kill a Mockingbird" by Harper Lee (281 pages)
Representation (using __repr__):
Book("The Great Gatsby", "F. Scott Fitzgerald", 180)
Book("To Kill a Mockingbird", "Harper Lee", 281)
Differences Between __str__
and __repr__
The main differences between __str__
and __repr__
are:
__str__
is meant for human-readable output, while __repr__
is meant for developers and debugging.
- If
__str__
is not defined but __repr__
is, Python will use __repr__
as a fallback for str()
or print()
.
__repr__
should ideally return a string that can recreate the object when passed to eval()
, although this is not always possible or necessary.
The eval()
Function with __repr__
When implemented correctly, the string returned by __repr__
can be used with eval()
to recreate the object. Let's see this in action:
-
Create a new file called repr_eval.py
:
-
Add the following code:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Point at ({self.x}, {self.y})"
def __repr__(self):
return f"Point({self.x}, {self.y})"
## Create a point
p1 = Point(3, 4)
## Get the repr string
repr_str = repr(p1)
print(f"Representation: {repr_str}")
## Use eval to recreate the object
p2 = eval(repr_str)
print(f"Recreated object: {p2}")
## Verify they have the same values
print(f"p1.x = {p1.x}, p1.y = {p1.y}")
print(f"p2.x = {p2.x}, p2.y = {p2.y}")
-
Save the file.
-
Run the script:
python3 repr_eval.py
You should see output like:
Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4
This demonstrates that we can recreate the original object using the string returned by __repr__
and the eval()
function.
When to Use Each Method
- Use
__init__
to set up the initial state of your objects.
- Use
__str__
to provide a human-readable representation for end-users.
- Use
__repr__
to provide a precise, unambiguous representation for developers and debugging.
Practical Exercise: A Complete Example
Let's put it all together by creating a Rectangle
class with all three special methods:
-
Create a new file called rectangle.py
:
-
Add the following code:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def __str__(self):
return f"Rectangle with width {self.width} and height {self.height}"
def __repr__(self):
return f"Rectangle({self.width}, {self.height})"
## Create rectangles
rect1 = Rectangle(5, 10)
rect2 = Rectangle(3, 7)
## Display information about the rectangles
print(f"Rectangle 1: {rect1}")
print(f"Area: {rect1.area()}")
print(f"Perimeter: {rect1.perimeter()}")
print(f"Representation: {repr(rect1)}")
print("\nRectangle 2: {0}".format(rect2))
print(f"Area: {rect2.area()}")
print(f"Perimeter: {rect2.perimeter()}")
print(f"Representation: {repr(rect2)}")
## Recreate a rectangle using eval
rect3 = eval(repr(rect1))
print(f"\nRecreated rectangle: {rect3}")
print(f"Is it the same area? {rect3.area() == rect1.area()}")
-
Save the file.
-
Run the script:
python3 rectangle.py
You should see output like:
Rectangle 1: Rectangle with width 5 and height 10
Area: 50
Perimeter: 30
Representation: Rectangle(5, 10)
Rectangle 2: Rectangle with width 3 and height 7
Area: 21
Perimeter: 20
Representation: Rectangle(3, 7)
Recreated rectangle: Rectangle with width 5 and height 10
Is it the same area? True
This example demonstrates how all three special methods (__init__
, __str__
, and __repr__
) work together to create a well-designed class.