Как переопределить методы `__str__` и `__repr__` для классов, которые используют составные или вложенные объекты?

Для классов с вложенными объектами, при переопределении __str__ и __repr__, следует учитывать форматирование вложенных объектов. Используйте str() или repr() соответственно для представления вложенных объектов.
Пример:
  
  class Inner:
      def __init__(self, value):
          self.value = value

      def __repr__(self):
          return f"Inner({self.value!r})"

  class Outer:
      def __init__(self, inner):
          self.inner = inner

      def __str__(self):
          return f"Outer object containing: {self.inner}"

      def __repr__(self):
          return f"Outer(inner={repr(self.inner)})"
  
  
Важно, чтобы __repr__ возвращал строку, которая, по возможности, позволит воссоздать объект. __str__ должен быть более читаемым для конечного пользователя. Используйте !r в f-строках для вызова repr().

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

Зачем это нужно?

  • __str__: Предназначен для *неформального* представления объекта, ориентированного на пользователя. Часто используется для печати объекта (например, через print()).
  • __repr__: Предназначен для *формального* представления объекта, полезного для отладки и воспроизведения. Идеально, чтобы результатом repr() было выражение Python, которое создаст идентичный объект (если это возможно).

Основные стратегии:

  1. Включение информации о вложенных объектах: Нужно решить, какую информацию из вложенных объектов стоит включать в строковое представление. Обычно достаточно идентификаторов или ключевых атрибутов.
  2. Использование str() и repr() для вложенных объектов: Вызовите str(вложенный_объект) в __str__ и repr(вложенный_объект) в __repr__, чтобы делегировать ответственность за форматирование вложенным объектам. Это обеспечит консистентность и повторное использование логики форматирования.
  3. Форматирование строк: Используйте f-строки (Python 3.6+) или .format() для создания читаемых строковых представлений.
  4. Ограничение длины: Для больших составных объектов можно обрезать строковое представление, чтобы избежать слишком длинных строк.
  5. Обработка рекурсии: Если вложенные объекты могут ссылаться друг на друга (рекурсивная структура данных), необходимо избегать бесконечной рекурсии при формировании строкового представления. Это можно сделать, отслеживая уже обработанные объекты и не обрабатывая их повторно.

Пример:


class Address:
    def __init__(self, street, city, zip_code):
        self.street = street
        self.city = city
        self.zip_code = zip_code

    def __str__(self):
        return f"{self.street}, {self.city}, {self.zip_code}"

    def __repr__(self):
        return f"Address(street='{self.street}', city='{self.city}', zip_code='{self.zip_code}')"


class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address

    def __str__(self):
        return f"Person: {self.name}, Age: {self.age}, Address: {self.address}"

    def __repr__(self):
        return f"Person(name='{self.name}', age={self.age}, address={repr(self.address)})"


address = Address("123 Main St", "Anytown", "12345")
person = Person("Alice", 30, address)

print(str(person)) # Output: Person: Alice, Age: 30, Address: 123 Main St, Anytown, 12345
print(repr(person)) # Output: Person(name='Alice', age=30, address=Address(street='123 Main St', city='Anytown', zip_code='12345'))

Разбор примера:

  • В классе Address методы __str__ и __repr__ возвращают строковое представление адреса.
  • В классе Person метод __str__ использует str(self.address), чтобы получить строковое представление адреса.
  • В классе Person метод __repr__ использует repr(self.address), чтобы получить формальное представление адреса. Важно использовать repr() для вложенных объектов в __repr__, чтобы представление было максимально информативным и позволяло воспроизвести объект.

Рекомендации:

  • Всегда предоставляйте реализацию __repr__. Это важный инструмент для отладки.
  • Предоставляйте реализацию __str__, когда нужно более "дружелюбное" представление для пользователей.
  • Протестируйте свои реализации __str__ и __repr__, чтобы убедиться, что они возвращают ожидаемые результаты.
0