__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, которое создаст идентичный объект (если это возможно).Основные стратегии:
str() и repr() для вложенных объектов:  Вызовите str(вложенный_объект) в __str__ и repr(вложенный_объект) в __repr__, чтобы делегировать ответственность за форматирование вложенным объектам. Это обеспечит консистентность и повторное использование логики форматирования..format() для создания читаемых строковых представлений.Пример:
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__, чтобы убедиться, что они возвращают ожидаемые результаты.