Вопрос о комбинировании __str__
и __repr__
касается того, как можно использовать эти два специальных метода Python для представления объекта в разных форматах, предназначенных для разных целей.
Ключевое различие:
__repr__
: Предназначен для однозначного представления объекта, часто в виде строки, которая может быть использована для воссоздания этого объекта. Идеально подходит для отладки и разработки. Если __str__
не определен, Python использует __repr__
в качестве запасного варианта.__str__
: Предназначен для удобочитаемого представления объекта, ориентированного на конечного пользователя. Он должен возвращать понятную и информативную строку.Способы комбинирования:
__str__
в __repr__
(когда это уместно): Если для объекта достаточно одного представления, и это представление однозначно и информативно, можно просто определить __repr__
, а __str__
не определять вовсе. Python автоматически использует __repr__
, если __str__
отсутствует. Это целесообразно для простых классов, где нет существенной разницы между представлением для разработчиков и для пользователей.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point(x={self.x}, y={self.y})"
# str(Point(1, 2)) вернет "Point(x=1, y=2)"
# repr(Point(1, 2)) вернет "Point(x=1, y=2)"
__repr__
будет предоставлять информацию, необходимую для воссоздания объекта или для отладки, а __str__
- более понятное и приятное для восприятия представление.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
def __str__(self):
return f"{self.name} is {self.age} years old."
person = Person("Alice", 30)
print(str(person)) # Output: Alice is 30 years old.
print(repr(person)) # Output: Person(name='Alice', age=30)
__repr__
внутри __str__
(реже, но возможно): Иногда __str__
может использовать __repr__
для части своей информации. Это может быть полезно, если требуется включить в удобочитаемый вывод какую-то техническую информацию, предоставленную __repr__
. Однако, следует избегать бесконечной рекурсии (__str__
вызывает __repr__
, который вызывает __str__
и т.д.).
class ComplexNumber:
def __init__(self, real, imag):
self.real = real
self.imag = imag
def __repr__(self):
return f"ComplexNumber(real={self.real}, imag={self.imag})"
def __str__(self):
return f"Complex number: {self.real} + {self.imag}j (Details: {repr(self)})"
complex_number = ComplexNumber(3, 4)
print(str(complex_number)) # Output: Complex number: 3 + 4j (Details: ComplexNumber(real=3, imag=4))
super()
для вызова __repr__
или __str__
из родительского класса, а затем добавить или изменить вывод.
class Animal:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"Animal(name='{self.name}')"
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name)
self.breed = breed
def __repr__(self):
return f"Dog(name='{self.name}', breed='{self.breed}')"
def __str__(self):
return f"This is a dog named {self.name}, a {self.breed}."
dog = Dog("Buddy", "Golden Retriever")
print(repr(dog)) # Output: Dog(name='Buddy', breed='Golden Retriever')
print(str(dog)) # Output: This is a dog named Buddy, a Golden Retriever.
Принципы хорошего дизайна:
__repr__
должен быть как можно более однозначным. В идеале, он должен возвращать строку, которую можно передать в eval()
для воссоздания объекта (но это не всегда возможно или безопасно).__str__
должен быть понятным и приятным для чтения. Он может быть менее подробным, чем __repr__
.__str__
не определен, Python использует __repr__
. Поэтому, если у вас есть только одно представление, определите __repr__
.__str__
и __repr__
.Таким образом, правильное использование __str__
и __repr__
позволяет предоставить разные представления объекта для разных целей, делая код более удобным для отладки и использования.