Implementando o Método __repr__
Além de __str__, o Python fornece outro método especial para representação de string: __repr__. Enquanto __str__ tem como objetivo fornecer uma representação legível por humanos, __repr__ se destina a fornecer uma representação inequívoca de um objeto que pode ser usada para recriar o objeto, se possível.
Entendendo o Método __repr__
O método __repr__ é chamado quando você usa a função repr() em um objeto ou quando você exibe um objeto em uma sessão interativa. Ele deve retornar uma string que, quando passada para eval(), criaria um objeto equivalente (quando possível).
Vamos atualizar nossa classe Book para incluir um método __repr__:
-
Abra book.py no editor.
-
Atualize o código para incluir um 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))
-
Salve o arquivo.
-
Execute o script:
python3 book.py
Você deve ver uma saída 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)
Diferenças Entre __str__ e __repr__
As principais diferenças entre __str__ e __repr__ são:
__str__ é destinado à saída legível por humanos, enquanto __repr__ é destinado a desenvolvedores e depuração.
- Se
__str__ não for definido, mas __repr__ for, o Python usará __repr__ como um fallback para str() ou print().
__repr__ deve, idealmente, retornar uma string que possa recriar o objeto quando passada para eval(), embora isso nem sempre seja possível ou necessário.
A Função eval() com __repr__
Quando implementado corretamente, a string retornada por __repr__ pode ser usada com eval() para recriar o objeto. Vamos ver isso em ação:
-
Crie um novo arquivo chamado repr_eval.py:
-
Adicione o seguinte 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}")
-
Salve o arquivo.
-
Execute o script:
python3 repr_eval.py
Você deve ver uma saída como:
Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4
Isso demonstra que podemos recriar o objeto original usando a string retornada por __repr__ e a função eval().
Quando Usar Cada Método
- Use
__init__ para configurar o estado inicial de seus objetos.
- Use
__str__ para fornecer uma representação legível por humanos para usuários finais.
- Use
__repr__ para fornecer uma representação precisa e inequívoca para desenvolvedores e depuração.
Exercício Prático: Um Exemplo Completo
Vamos juntar tudo criando uma classe Rectangle com todos os três métodos especiais:
-
Crie um novo arquivo chamado rectangle.py:
-
Adicione o seguinte 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()}")
-
Salve o arquivo.
-
Execute o script:
python3 rectangle.py
Você deve ver uma saída 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 exemplo demonstra como todos os três métodos especiais (__init__, __str__ e __repr__) trabalham juntos para criar uma classe bem projetada.