Как обрабатывать нечисловые типы данных в функции
Что проверяют
Как вы проектируете контракт функции: что она принимает, что делает с чужим типом и как сообщает об ошибке. Следом обычно спрашивают, чем отличаются подходы EAFP и LBYL и что будет, если передать True.
Как отвечать
Начните с решения, которое принимается до кода: функция либо строгая и отказывает всему нечисловому, либо терпимая и пытается привести значение. От этого зависит всё остальное.
- сигнатура и аннотации:
def scale(x: float) -> float. По правилам типизацииintпринимается там, где ждутfloat. Сами аннотации во время выполнения ничего не проверяют, это делают mypy или pyright, а на границе с внешними данными валидирует Pydantic; - проверка типа:
isinstance(x, numbers.Real)покрываетint,float,Fractionи пользовательские числовые типы, аDecimalвRealне входит, его придётся указать отдельно. Для разного поведения по типам удобнееfunctools.singledispatchилиmatchс шаблономcase int() | float():, чем лестница изelif; - EAFP: питоничный путь для приведения это
float(x)внутриtryс перехватом(TypeError, ValueError).floatсправится с" 5 "и"1e3", но не с"1,5"; - ошибки: неверный тип сообщается через
TypeError, неверное значение того же типа черезValueError. Функция не возвращает строку «некорректные данные» вместо числа; - краевые случаи:
float("nan")иfloat("inf")проходят преобразование, поэтому при необходимости добавьтеmath.isfinite.
Если данные табличные, в pandas есть pd.to_numeric(s, errors="coerce"): всё непреобразуемое станет NaN одним вызовом.
Где спотыкаются
Забывают, что bool это подкласс int. isinstance(True, int) вернёт True, и scale(True) тихо посчитает единицу. Если это недопустимо, bool исключают явно.
Возвращают из функции то число, то строку с ошибкой. Вызывающий код падает далеко от места проблемы, а в аннотации появляется float | str, которую каждый вызывающий обязан разбирать.
Ловят голый except: или except Exception. Так проглатываются и настоящие баги, вплоть до опечатки в имени переменной.