⚖️ Старый vs Новый: выбор подхода

⚡ Кратко

Сравните два подхода и выбирайте более ясный и безопасный вариант, сохраняя старый синтаксис только для чтения существующего кода.

Абстрактный класс vs обычный класс

КритерийОбычный классАбстрактный класс
Создание объектовРазрешеноЗапрещено
НазначениеГотовая реализацияИнтерфейс + общая логика
Контроль наследниковНе гарантирует переопределениеОбязывает реализовать методы
# Обычный класс — можно создать, даже если метод пуст
class Worker:
    def work(self): pass

w = Worker()  # OK

# Абстрактный класс — защита от незавершённых объектов
from abc import ABC, abstractmethod
class Worker(ABC):
    @abstractmethod
    def work(self): pass

# w = Worker()  # TypeError

Встроенные vs пользовательские исключения

ПодходПлюсыМинусы
Встроенные (ValueError и др.)Не нужно ничего создаватьНе передаёт специфику домена
Пользовательские от ExceptionЯсно отделяет ошибки логикиБольше кода
Пользовательские от подкласса (ValueError)Точность + совместимостьНужно выбрать правильного родителя

Ручные методы vs магические методы

ПодходСинтаксисКогда использовать
Обычные методыa.equals(b)Когда нужен явный вызов метода
Магические методыa == b, len(a)Когда объект ведёт себя как встроенный тип
# Вручную
if book1.equals(book2):
    print("Равны")

# Через dunder
if book1 == book2:
    print("Равны")

Все сравнения vs @total_ordering

ПодходКоличество методовРиск ошибки
Ручная реализация всех методов6Высокий (противоречивые правила)
@total_ordering2 (__eq__ + один)Низкий, Python генерирует остальное
from functools import total_ordering

@total_ordering
class Person:
    def __eq__(self, other):
        ...
    def __lt__(self, other):
        ...
# Остальные операторы работают автоматически