Реализация метода __repr__
В дополнение к __str__, Python предоставляет еще один специальный метод для строкового представления: __repr__. В то время как __str__ предназначен для предоставления удобочитаемого представления, __repr__ предназначен для предоставления однозначного представления объекта, которое можно использовать для повторного создания объекта, если это возможно.
Понимание метода __repr__
Метод __repr__ вызывается, когда вы используете функцию repr() для объекта или когда вы отображаете объект в интерактивной сессии. Он должен возвращать строку, которая при передаче в eval() создаст эквивалентный объект (если это возможно).
Давайте обновим наш класс Book, чтобы включить метод __repr__:
-
Откройте book.py в редакторе.
-
Обновите код, чтобы включить метод __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))
-
Сохраните файл.
-
Запустите скрипт:
python3 book.py
Вы должны увидеть вывод, подобный:
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)
Различия между __str__ и __repr__
Основные различия между __str__ и __repr__ заключаются в следующем:
__str__ предназначен для удобочитаемого вывода, в то время как __repr__ предназначен для разработчиков и отладки.
- Если
__str__ не определен, но определен __repr__, Python будет использовать __repr__ в качестве резервного варианта для str() или print().
__repr__ в идеале должен возвращать строку, которая может воссоздать объект при передаче в eval(), хотя это не всегда возможно или необходимо.
Функция eval() с __repr__
При правильной реализации строка, возвращаемая __repr__, может быть использована с eval() для повторного создания объекта. Давайте посмотрим это в действии:
-
Создайте новый файл с именем repr_eval.py:
-
Добавьте следующий код:
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}")
-
Сохраните файл.
-
Запустите скрипт:
python3 repr_eval.py
Вы должны увидеть вывод, подобный:
Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4
Это демонстрирует, что мы можем воссоздать исходный объект, используя строку, возвращаемую __repr__, и функцию eval().
Когда использовать каждый метод
- Используйте
__init__ для установки начального состояния ваших объектов.
- Используйте
__str__ для предоставления удобочитаемого представления для конечных пользователей.
- Используйте
__repr__ для предоставления точного, однозначного представления для разработчиков и отладки.
Практическое упражнение: Полный пример
Давайте объединим все это, создав класс Rectangle со всеми тремя специальными методами:
-
Создайте новый файл с именем rectangle.py:
-
Добавьте следующий код:
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()}")
-
Сохраните файл.
-
Запустите скрипт:
python3 rectangle.py
Вы должны увидеть вывод, подобный:
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
Этот пример демонстрирует, как все три специальных метода (__init__, __str__ и __repr__) работают вместе для создания хорошо спроектированного класса.