4 .2 . Преобразование строк (каждому классу по __repr__)
109
В случае таких вопросов обычно неплохо взглянуть на то, что делает
стандартная библиотека Python. Самое время поставить еще один экс-
перимент. Мы создадим объект
datetime.date
и выясним, каким образом
в нем используются методы
__repr__
и
__str__
для управления преоб-
разованием строк:
>>>
import datetime
>>> today = datetime.date.today()
Результат метода
__str__
объекта даты должен быть прежде всего
удо-
бочитаемым. Он призван возвращать легко воспринимаемое человеком
сжатое текстовое представление — то, что вы спокойно можете показать
пользователю. По этой причине, когда мы вызываем функцию
str()
с объ-
ектом даты, мы получаем нечто похожее на формат даты по ISO:
>>> str(today)
'2017-02-02'
В случае с методом
__repr__
идея состоит в том, что его результат должен
быть прежде всего
однозначным. Результирующее строковое значение
больше предназначено для разработчиков как средство отладки. И в
связи с этим он должен максимально четко выражать то, чем этот объект
является. Именно поэтому при вызове функции
repr()
с объектом вы
получите более подробный результат. Он даже будет содержать полное
имя модуля и класса:
>>> repr(today)
'datetime.date(2017, 2, 2)'
Возвращаемое методом
__repr__
строковое значение можно скопировать
и вставить в консоль интерпретатора и исполнить его как допустимый
фрагмент кода Python, чтобы воссоздать оригинальный объект даты. Этот
изящный подход и хороший целевой ориентир следует иметь в виду при
написании
своих собственных функций
repr
.
С другой стороны, я полагаю, что довольно-таки трудно найти приме-
нение такой функции на практике. Она не будет стоить затраченных на
нее усилий и просто создаст для вас дополнительную работу. Мое эмпи-
рическое правило заключается в том, чтобы делать свои строки
__repr__
110 Глава 4 •
Классы и ООП
однозначными и полезными для разработчиков, но я не рассчитываю,
что они смогут восстанавливать полное состояние объекта.
Почему каждый класс нуждается в __repr__
Если опустить метод
__str__
, то Python в поисках
__str__
отыграет назад
к результату
__repr__
. По этой причине я рекомендую добавлять в свои
классы всегда, по крайней мере, метод
__repr__
. Это обеспечит полезный
результат преобразования строк почти во всех случаях при минимуме
работы по его реализации.
Ниже показано, как можно быстро и эффективно добавить в свои классы
элементарную поддержку преобразования строк. Для нашего класса
Car
мы могли бы начать
с приведенного ниже метода
__repr__
:
def __repr__(self):
return f'Car({self.color!r}, {self.mileage!r})'
Обратите внимание на то, что я использую флаг преобразования
!r
,
тем самым гарантируя, что в выводимом строковом значении вместо
str(self.color)
и
str(self.mileage)
будут использованы
repr(self.
color)
и
repr(self.mileage)
.
Это работает безупречно, но оборотной стороной является то, что мы по-
вторили имя класса в форматной строке. Для того чтобы избежать этого
повторения, здесь можно применить трюк с использованием атрибута
__calss__.__name__
объекта. Данный атрибут всегда будет зеркально
отображать имя класса в виде строки.
Преимущество состоит в том, что вам не придется модифицировать реа-
лизацию метода
__repr__
, когда имя класса изменится. Это позволяет
беспрепятственно придерживаться принципа DRY, то есть
«не повто-
ряйся»:
def __repr__(self):
return (f'{self.__class__.__name__}('
f'{self.color!r}, {self.mileage!r})')
4 .2 . Преобразование строк (каждому классу по __repr__)
111
Оборотной стороной этой реализации является то, что форматная строка
довольно длинная и громоздкая. Но при условии выверенного формати-
рования вы сможете сохранить свой исходный код аккуратным и соот-
ветствующим правилам PEP 8.
Во время инспектирования объекта или непосредственного вызова функ-
ции
repr()
с объектом при наличии приведенной выше реализации метода
__repr__
мы получаем полезный результат:
>>> repr(my_car)
Do'stlaringiz bilan baham: