__str__
и __repr__
:
super().__str__()
или super().__repr__()
для вызова родительского метода.__str__
или __repr__
косвенно.repr()
или str()
напрямую, а не f-строки.Предотвращение бесконечных рекурсий в методах __str__
и __repr__
критически важно, так как они часто используются для отладки и логирования. Неправильная реализация может привести к переполнению стека и падению программы.
Проблема: Бесконечная рекурсия возникает, когда реализация __str__
или __repr__
вызывает саму себя напрямую или косвенно.
Основные стратегии предотвращения:
Использовать super().__str__()
или super().__repr__()
: Вместо прямой работы с атрибутами текущего объекта, можно вызвать реализацию метода у родительского класса. Это позволяет избежать непосредственного вызова текущего метода.
class MyClass:
def __init__(self, value):
self.value = value
def __str__(self):
return f"MyClass({self.value})" # Потенциально опасно, если MyClass вызывает str(self)
class MySubClass(MyClass):
def __init__(self, value, extra):
super().__init__(value)
self.extra = extra
def __str__(self):
return f"MySubClass({self.value}, {self.extra})" # Безопасно
def __repr__(self):
return f"MySubClass(value={self.value}, extra={self.extra})" #Безопасно
Использовать object.__str__()
или object.__repr__()
: Если у класса нет явно заданного родителя, можно использовать реализацию, предоставленную базовым классом object
. Это гарантирует, что метод не будет вызывать саму себя.
class MyClass:
def __init__(self, value):
self.value = value
def __str__(self):
return object.__str__(self) # Безопасно, вызов базового object.__str__()
def __repr__(self):
return f"<MyClass object at {hex(id(self))}>" # Безопасно
Избегать использования str(self)
или repr(self)
внутри методов: Эти вызовы, даже косвенные, приводят к рекурсивному вызову текущего метода. Вместо этого, обращайтесь напрямую к атрибутам объекта и форматируйте их.
class MyClass:
def __init__(self, value):
self.value = value
def __str__(self):
# ОПАСНО: вызывает рекурсию
# return f"Value: {str(self)}"
# БЕЗОПАСНО: напрямую форматирует атрибут
return f"Value: {self.value}"
def __repr__(self):
# ОПАСНО: вызывает рекурсию
# return f"MyClass({repr(self)})"
#БЕЗОПАСНО:
return f"MyClass(value={self.value})"
Ограничить глубину рекурсии (менее предпочтительный метод): Хотя это и не лучшее решение, можно временно увеличить лимит рекурсии с помощью `sys.setrecursionlimit()`, но это просто откладывает проблему, а не решает её. Это обычно является признаком плохого дизайна.
import sys
class MyClass:
def __init__(self, value):
self.value = value
def __str__(self):
# Неправильный подход: использование sys.setrecursionlimit - это "костыль"
# sys.setrecursionlimit(10000) #Очень плохо.
# return f"Value: {str(self)}" # Вызовет рекурсию, но теперь больше лимит.
return f"Value: {self.value}" # Правильный подход
Лучшие практики:
__repr__
должен давать недвусмысленное строковое представление объекта, которое в идеале можно использовать для его воссоздания (например, в виде вызова конструктора).__str__
должен давать удобное для пользователя строковое представление.__repr__
должна быть всегда безопасной (не вызывать исключений). Если ее невозможно сделать безопасной, лучше предоставить fallback-реализацию, использующую object.__repr__
__str__
и __repr__
.__str__
и __repr__
, особенно при наследовании.