class CustomError(Exception):
    def __init__(self, code, message="Ошибка"):
        self.code = code
        self.message = message
    def __str__(self):
        return f"{self.code} - {self.message}"
try:
    raise CustomError(500)
except CustomError as e:
    print(e)
500 - Ошибка. Сначала поднимается исключение CustomError с кодом 500.  Затем, блок except ловит это исключение и выводит строку, возвращаемую методом __str__ класса CustomError.
Описание кода:
Код определяет пользовательское исключение CustomError, наследующееся от базового класса Exception.  Это исключение имеет два атрибута: code (код ошибки) и message (сообщение об ошибке).  Конструктор __init__ инициализирует эти атрибуты, а метод __str__ переопределяется для возврата строкового представления ошибки в формате "code - message".
В блоке try...except происходит следующее:
try: В блоке try возбуждается исключение CustomError с кодом 500 и сообщением по умолчанию "Ошибка".except CustomError as e: Если в блоке try возбуждается исключение типа CustomError, оно перехватывается блоком except. Перехваченное исключение присваивается переменной e.print(e): В блоке except вызывается функция print(e), которая выводит строковое представление исключения e.  Так как переопределен метод __str__, будет выведено значение в формате "code - message".Результат выполнения:
Код выведет на консоль следующую строку:
500 - ОшибкаПояснения:
__str__ метод: Переопределение метода __str__ необходимо для удобного представления исключения в виде строки.  Это особенно полезно при отладке и логировании.try...except блок:  Блок try...except используется для обработки исключений. Код, который может вызвать исключение, помещается в блок try, а код, который обрабатывает исключение, помещается в блок except.