Implementando el Método __repr__
Además de __str__, Python proporciona otro método especial para la representación de cadenas: __repr__. Mientras que __str__ está destinado a proporcionar una representación legible por humanos, __repr__ tiene como objetivo proporcionar una representación inequívoca de un objeto que se puede usar para recrear el objeto si es posible.
Entendiendo el Método __repr__
El método __repr__ se llama cuando usa la función repr() en un objeto o cuando muestra un objeto en una sesión interactiva. Debe devolver una cadena que, cuando se pasa a eval(), crearía un objeto equivalente (cuando sea posible).
Actualicemos nuestra clase Book para incluir un método __repr__:
-
Abra book.py en el editor.
-
Actualice el código para incluir un método __repr__:
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))
-
Guarde el archivo.
-
Ejecute el script:
python3 book.py
Debería ver una salida como:
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)
Diferencias entre __str__ y __repr__
Las principales diferencias entre __str__ y __repr__ son:
__str__ está destinado a la salida legible por humanos, mientras que __repr__ está destinado a desarrolladores y depuración.
- Si
__str__ no está definido pero __repr__ sí, Python usará __repr__ como respaldo para str() o print().
__repr__ idealmente debería devolver una cadena que pueda recrear el objeto cuando se pasa a eval(), aunque esto no siempre es posible o necesario.
La Función eval() con __repr__
Cuando se implementa correctamente, la cadena devuelta por __repr__ se puede usar con eval() para recrear el objeto. Veamos esto en acción:
-
Cree un nuevo archivo llamado repr_eval.py:
-
Agregue el siguiente código:
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}")
-
Guarde el archivo.
-
Ejecute el script:
python3 repr_eval.py
Debería ver una salida como:
Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4
Esto demuestra que podemos recrear el objeto original usando la cadena devuelta por __repr__ y la función eval().
Cuándo Usar Cada Método
- Use
__init__ para configurar el estado inicial de sus objetos.
- Use
__str__ para proporcionar una representación legible por humanos para los usuarios finales.
- Use
__repr__ para proporcionar una representación precisa e inequívoca para desarrolladores y depuración.
Ejercicio Práctico: Un Ejemplo Completo
Pongamos todo junto creando una clase Rectangle con los tres métodos especiales:
-
Cree un nuevo archivo llamado rectangle.py:
-
Agregue el siguiente código:
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()}")
-
Guarde el archivo.
-
Ejecute el script:
python3 rectangle.py
Debería ver una salida como:
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
Este ejemplo demuestra cómo los tres métodos especiales (__init__, __str__ y __repr__) funcionan juntos para crear una clase bien diseñada.