Магические методы: __str__, __repr__, __len__, __abs__

На этом занятии я расскажу о, так называемых, магических методах, которые предопределены в каждом классе и записываются через два подчеркивания вначале и в конце имен, например, так:

__str__, __repr__

Еще их называют

dunder-методами (от англ. сокращения double underscope)

Магические методы __str__ и __repr__

Каждый из магических методов автоматически срабатывает в определенный момент времени, например:

  • __str__() – магический метод для отображения информации об объекте класса для пользователей (например, для функций print, str);
  • __repr__() – магический метод для отображения информации об объекте класса в режиме отладки (для разработчиков).

Чтобы лучше понять, как работают эти методы, объявим класс для описания кошек:

class Cat:
    def __init__(self, name):
        self.name = name

Если теперь перейти в консоль Python, импортировать этот класс из нашего текущего модуля ex1:

from ex1 import Cat

и создать его экземпляр:

cat = Cat('Васька')

то при выводе cat, увидим служебную информацию:

<ex1.Cat object at 0x0495D028>

Если мы хотим ее как-то переопределить и отображать информацию в другом виде, формате, то как раз и используются магические методы __str__ и __repr__. Давайте для начала переопределим метод __repr__ и посмотрим как это отразится на выводе служебной информации о классе:

    def __repr__(self):
        return f"{self.__class__}: {self.name}"

Обратите внимание, этот метод должен возвращать строку, поэтому здесь записан оператор return и формируемая строка. Что именно возвращать, мы решаем сами, в данном случае – это название класса и имя кошки.

Перезапустим консоль и снова импортируем измененный класс Cat. И, смотрите, теперь при создании экземпляра мы видим другую информацию при его выводе:

<class 'ex1.Cat'>: Васька

Как раз то, что определили в магическом методе __repr__. То же самое увидим и при использовании функции print и str. По идее, здесь должен отрабатывать другой магический метод __str__, но так как он у нас еще не переопределен, то автоматически выполняется метод __repr__.

Давайте добавим второй магический метод __str__ и посмотрим, как это повлияет на отображение данных:

    def __str__(self):
        return f"{self.name}"

Выполним повторное импортирование класса Cat, создадим его экземпляр и при отображении по ссылке:

cat

по-прежнему будем видеть служебную информацию от метода __repr__.  Однако, если выполнить отображение экземпляра класса через print или str, то будет срабатывать уже второй метод __str__. Вот в этом отличие этих двух магических методов.

Магические методы __len__ и __abs__

Следующие два магических метода:

  • __len__() – позволяет применять функцию len() к экземплярам класса;
  • __abs__() - позволяет применять функцию abs() к экземплярам класса.

Их использование достаточно простое и очевидное. Давайте для примера представим, что у нас есть класс Point, который может хранить произвольный вектор координат точки и определим его так:

class Point:
    def __init__(self, *args):
        self.__coords = args

А, далее, по программе нам бы хотелось определять размерность координат с помощью функции len(), следующим образом:

p = Point(1, 2)
print( len(p) )

Если сейчас запустить программу, то увидим ошибку, так как функция len не применима к экземплярам классов по умолчанию. Как вы уже догадались, чтобы изменить это поведение, можно переопределить магический метод __len__() и в нашем случае это можно сделать так:

    def __len__(self):
        return len(self.__coords)

Смотрите, мы здесь возвращаем размер списка __coords и если после этого запустить программу, то как раз это значение и будет выведено в консоль. То есть, магический метод __len__ указал, что нужно возвращать, в момент применения функции len() к экземпляру класса. Как видите, все просто и очевидно.

Следующий магический метод __abs__ работает аналогичным образом, только активируется в момент вызова функции abs для экземпляра класса, например, так:

print( abs(p) )

Опять же, если сейчас выполнить программу, то увидим ошибку, т.к. функция abs не может быть напрямую применена к экземпляру. Но, если переопределить магический метод:

    def __abs__(self):
        return list( map(abs, self.__coords) )

который возвращает список из абсолютных значений координат точки, то программа отработает в штатном режиме и мы увидим ожидаемый результат.

Вот так можно использовать эти два магических метода. Я, надеюсь, из этого занятия вы узнали, как применять четыре магических метода:

__str__, __repr__, __len__, __abs__

Видео по теме