ООП на Python, ч. 3. Методы __str__ и __repr__.
Продолжаем цикл постов об ООП на Python. В прошлый раз мы говорили о статических и классовых методах. Сегодня разберем методы __str__
и __repr__
.
Реклама
Продолжаем цикл постов об ООП на Python. В прошлый раз мы говорили о статических и классовых методах. Сегодня разберем методы __str__
и __repr__
.
В рамках первого поста об ООП на Python мы уже рассматривали метод __init__
. Все три эти метода (__str__
, __repr__
и __init__
), как нетрудно заметить, объединяют по два нижних подчеркивания до и после собственно названия метода. Такой синтаксис обозначает так называемые магические (или dunder - от английского “double under(scores)” - “двойное нижнее подчёркивание”) методы. Они используются для определения специального поведения объектов и вызываются во время использования стандартных операций (сложение, вычитание, доступ к атрибутам и др.).
Метод __str__().
Наверняка вам доводилось создать объект какого-нибудь класса и сразу же этот объект распечатать методом print()
. Чаще всего результат выглядел немного загадочно. Давайте создадим свой собственный класс и попробуем распечатать его объект.
Используем класс футбольного клуба из предыдущего поста:
class FootballClub:
def __init__(self, name, description=""):
self.name = name
self.description = description
def describe_club(self):
msg = f"{self.name}: {self.description}"
print(msg)
club = FootballClub(“Манчестер Юнайтед”, “Манчестер, Англия”)
print(club)
Вместо того, чтобы вызвать метод describe_club()
, мы просто распечатаем вновь созданный объект нашего класса. Результат получается примерно таким:
<__main__.FootballClub object at 0x1050effd0>
Если явным образом не оповестить Python о том, как должно выглядеть представление объекта в виде строки, будет распечатано сообщение, содержащее следующую информацию:
• Имя запускаемого файла
• Имя класса, который использовался для создания объекта
• Ячейка памяти, в которой хранится объект.
Эта информация бывает полезна в процессе отладки кода, когда нужна заниматься поиском и устранением багов, но простому пользователю она мало о чем говорит. И выглядит, откровенно говоря, не очень читабельно для неподготовленного человека. Метод __str__()
позволяет переопределить, что должно выводиться на печать, когда по отношению к объекту класса используется метод print()
.
Переопределим метод __str__()
для нашего класса:
class FootballClub:
def __init__(self, name, description=""):
self.name = name
self.description = description
def describe_club(self):
msg = f"{self.name}: {self.description}"
print(msg)
def __str__(self):
return self.name
club = FootballClub("Манчестер Юнайтед", "Манчестер, Англия")
print(club)
И результат выполнение кода теперь выглядит куда дружелюбнее:
Манчестер Юнайтед
Метод __str__()
используется не только для вывода строк на печать в терминале. Многие панели управления “ищут” именно __str__()
для отображения данных, связанных с экземплярами того или иного класса. Так, к примеру, происходит в админ панели Django, поэтому в большинстве случаев метод __str__()
определен в классах моделей.
Рассмотрим пример из официального туториала по созданию приложений на Django. В нём разбирается пример класса модели Question
- вопроса в рамках приложения для проведения анкетирования. Код модели выглядит следующим образом:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date_published')
def __str__(self):
return self.question_text
Так как метод __str__()
переопределен, в админ панели будет выводиться непосредственно сам вопрос. Если бы метод не переопределялся, то там бы виднелось что-то вроде Question object (1)
. Дело в том, что в Django все модели наследуются от базового класса модели в модуле django.db.models
, где метод __str__()
определяется следующим образом:
def __str__(self):
return "%s object (%s)" % (self.__class__.__name__, self.pk)
Таким образом, на печать выводится название класса, слово object
и первичный ключ объекта в скобках.
Метод __repr__().
Прежде чем пускаться в объяснения работы метода __repr__()
, разберёмся с тем, что же он делает. Для этого откроем терминал и зайдём в папку, где лежит наш файл с классом футбольного клуба, который мы создали ранее. Таким образом мы сможем получить доступ к классам и функциям нашего файла прямо из терминала:
$ ls
football.py
$ python
>>> from football import FootballClub
>>> club = FootballClub("Манчестер Юнайтед")
>>> print(club)
Манчестер Юнайтед
>>>
Сначала мы импортируем класс FootballClub
. Затем создаем объект этого класса, передав в него название клуба ("Манчестер Юнайтед”), и записываем его в переменную club
. Далее выводим на печать содержимое этой самой переменной. Срабатывает метод __str__()
, который мы создали, и мы видим название клуба. А что случится, если мы в терминале просто введем имя переменной, не передавая его предварительно в метод print()
?
>>> club
<football.FootballClub object at 0x10bdc8040>
На печать выводится название модуля, название класса, слово object
и адрес ячейки, в которую он записан.
Если вы хотите, чтобы в этот момент в терминале (или ином служебном окружении) выводилась другая информация, необходимо переопределить метод __repr__()
. Но прежде чем бросаться это делать, прочитаем официальную документацию к этому методу:
“Если это возможно, вывод должен быть в виде выражения Python, которое можно использовать для воссоздания объекта с тем же значением (при условии наличия подходящего окружения). Если это невозможно, вывод должен быть в виде строки с текстом в таком формате <…полезная информация…>. Возвращаемое значение должно быть строкой Python.
Вывод метода обычно используется для дебаггинга, поэтому важно, чтобы он был информативным и недвусмысленным”.
Альтернативный вывод метода __repr__()
должен помочь заново создать эквивалентный объект данного класса. Давайте добавим метод __repr__()
в класс FootballClub
:
class FootballClub:
def __init__(self, name, description=""):
self.name = name
self.description = description
def describe_club(self):
msg = f"{self.name}: {self.description}"
print(msg)
def __str__(self):
return self.name
def __repr__(self):
if self.description:
return (f"FootballClub(name={self.name}, "
f"description={self.description})")
else:
return f"FootballClub(name={self.name})"
Чтобы этот код сработал, надо заново открыть окно терминала и повторить весь порядок ввода:
$ python
>>> from football import FootballClub
>>> club = FootballClub("Манчестер Юнайтед")
>>> club
FootballClub("Манчестер Юнайтед")
На этот раз выводится не стандартная запись, а вывод нашего перезаписанного метода __repr__()
: строка, позволяющая создать объект, полностью эквивалентный тому, в котором и был вызван метод __repr__()
.
В нашем примере срабатывал кусок кода, записанный в else
. Если мы добавим в наш объект описание (description
), вывод будет уже другим:
$ python
>>> from football import FootballClub
>>> club = club = FootballClub("Манчестер Юнайтед", "Английский футбольный клуб")
>>> club
FootballClub(name=Манчестер Юнайтед, description=Английский футбольный клуб)
Python вызывает метод __repr__()
, когда генерирует вывод для дебаггинга. В момент дебаггинга кусок кода, который позволит сгенерировать объект, задействованный в проблемном коде.
В следующий раз поговорим о методе __new__()
.
Все статьи