__str__, __repr__ и __eq__
В прошлом уроке мы заполняли карточки по чертежу. Теперь представьте, что карточку нужно кому-то показать или сравнить с другой. Человек сначала прочтёт, что на ней написано. Python так не умеет: экземпляр нового класса он не может ни показать по-человечески, ни сравнить по содержимому.
Давайте посмотрим на точку с координатами:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
print(p == Point(1, 2)) # False
print(p)Здесь две неожиданности:
p == Point(1, 2)даётFalse, хотя координаты одинаковые;- последняя строка печатает что-то вроде
<__main__.Point object at 0x7f3a2c1b2d50>: имя класса и адрес объекта в памяти, который меняется от запуска к запуску.
По умолчанию == проверяет, тот ли это объект, а не одинаковы ли координаты. Оба поведения класс может заменить своими.
Специальные методы
Методы, имя которых начинается и заканчивается двумя подчёркиваниями, — специальные методы. Обычно их не вызывают по имени. Python вызывает их сам в определённых ситуациях, как __init__ при создании экземпляра.
Для показа объекта у класса два таких метода. Они отвечают на разные вопросы, поэтому разберём каждый.
__str__ — для людей
Метод __str__ возвращает строку для читателя программы. Её показывают print, str() и f-строки. Это как подпись на карточке крупными буквами: «точка (1, 2)».
__repr__ — для программиста
Метод __repr__ возвращает строку для программиста. Её показывает функция repr(), а ещё список, кортеж и словарь для своих элементов. Текст __repr__ обычно похож на вызов, который создаёт такой же объект: Point(1, 2).
Соберём оба метода в одном классе:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __str__(self):
return f"({self.x}, {self.y})"
def __repr__(self):
return f"Point({self.x}, {self.y})"
p = Point(1, 2)
print(p) # (1, 2)
print([p, Point(3, 4)]) # [Point(1, 2), Point(3, 4)]
print(f"p = {p}", repr(p)) # p = (1, 2) Point(1, 2)Разберём вывод по строкам:
print(p)вызывает__str__и печатает(1, 2).print([p, Point(3, 4)])печатает список, а список показывает каждый элемент через__repr__.- f-строка берёт
__str__, аrepr(p)—__repr__.
Строку __init__ здесь короче записывает распаковка: self.x, self.y = x, y. Кавычки в ['a'] — тоже часть __repr__: так показывает себя строка.
Порядок методов в классе не важен. Если __str__ в классе нет, print использует __repr__. Поэтому, если пишете только один из двух, пишите __repr__.
Сравнение через __eq__
Представьте два экземпляра одной книги. Текст в них одинаковый, но на полке стоят две книги, а не одна. В Python это два разных вопроса:
a is bспрашивает, один ли это объект;a == bспрашивает, равны ли объекты по содержимому.
Без своего метода == у класса отвечает на первый вопрос, как is: равен только сам объект. Метод __eq__(self, other) задаёт сравнение по содержимому. Python вызывает его для a == b, передавая a в self и b в other:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p = Point(1, 2)
print(p == Point(1, 2), p is Point(1, 2)) # True False
print(p != Point(3, 4)) # True__eq__ возвращает True или False. Оператор != Python выводит из __eq__ сам. На is метод не влияет: два вызова Point(1, 2) создают разные объекты.
В other попадает любой объект справа от ==. Сравнение p == 5 здесь остановится с AttributeError, потому что у числа нет атрибута x.
Этот вопрос часто задают на собеседовании: «Чем == отличается от is?» is сравнивает объекты по тождеству, то есть проверяет, один ли это объект. == вызывает __eq__ и сравнивает по содержимому, если класс его задал.
Частая ошибка
__str__ возвращает не строку, а число:
class Point:
def __init__(self, x):
self.x = x
def __str__(self):
return self.x # ✗
print(Point(1))TypeError: __str__ returned non-string (type int)print требует, чтобы __str__ и __repr__ возвращали str. Соберите строку f-строкой: return f"{self.x}".
Главное
__str__даёт строку дляprint,str()и f-строк, а__repr__— для программиста и для элементов списка.- Без
__str__функцияprintиспользует__repr__. - Без
__eq__оператор==сравнивает объекты какis;__eq__задаёт сравнение по содержимому. __str__и__repr__обязаны возвращатьstr.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.