Специальные методы: __repr__, __str__, __eq__, __len__
Содержание курса
Реализация всех четырёх методов в классе Playlist и проверка
Соберём всё вместе в одном классе. Playlist хранит имя плейлиста и список треков — достаточно, чтобы осмысленно реализовать каждый из четырёх методов.
class Playlist:
def __init__(self, name: str, tracks: list[str]):
self.name = name
self.tracks = tracks
def __repr__(self) -> str:
return f"Playlist(name={self.name!r}, tracks={self.tracks!r})"
def __str__(self) -> str:
return f"Playlist '{self.name}' ({len(self.tracks)} tracks)"
def __eq__(self, other: object) -> bool:
if not isinstance(other, Playlist):
return NotImplemented
return self.name == other.name and self.tracks == other.tracks
def __len__(self) -> int:
return len(self.tracks)
В этом классе __repr__ и __eq__ используют одни и те же атрибуты (name и tracks), поэтому их поведение согласовано: объекты с одинаковыми данными имеют одинаковый repr и равны через ==. Это хорошая практика проектирования — держать repr и eq в согласии, — но Python не обеспечивает её автоматически. В другом классе одинаковый repr не обязан означать равенство, и равные объекты не обязаны иметь одинаковый repr. __str__ намеренно лаконичнее: показывает только количество треков, а не их содержимое. Порядок треков при сравнении важен, потому что self.tracks — список.
p1 = Playlist("Rock", ["Song A", "Song B"])
p2 = Playlist("Rock", ["Song A", "Song B"])
p3 = Playlist("Jazz", ["Blue Moon"])
empty = Playlist("Empty", [])
repr(p1)
# "Playlist(name='Rock', tracks=['Song A', 'Song B'])"
print(p1)
# Playlist 'Rock' (2 tracks)
f"{p1}"
# "Playlist 'Rock' (2 tracks)"
[p1, p3]
# [Playlist(name='Rock', tracks=['Song A', 'Song B']), Playlist(name='Jazz', tracks=['Blue Moon'])]
p1 == p2 # True
p1 == p3 # False
p1 == 42 # False — благодаря NotImplemented, который мы разобрали выше
p4 = Playlist("Rock", ["Song C", "Song D"])
p1 == p4 # False
repr(p4)
# "Playlist(name='Rock', tracks=['Song C', 'Song D'])"
len(p1) # 2
len(empty) # 0
bool(p1) # True
bool(empty) # False
Обратите внимание на пару p1 / p4: имя одинаковое, треки разные — __eq__ возвращает False, а __repr__ наглядно показывает разницу. Если бы __repr__ отображал только количество треков, оба плейлиста выглядели бы идентично в отладчике, хотя данные различаются. Именно поэтому здесь показываем полный список. Контейнер [p1, p3] использует __repr__ элементов — и благодаря этому в выводе видны реальные данные, а не лаконичные строки из __str__.
В следующем уроке @dataclass сгенерирует __repr__ и __eq__ автоматически; ручная реализация помогает понимать, что именно генерируется и почему.
