Как я должен напечатать подсказку целочисленной переменной, которая также может быть бесконечной? - PullRequest
0 голосов
/ 20 февраля 2019

В поисках этой темы я натолкнулся на следующее: Как представить целочисленную бесконечность?

Я согласен с Мартином Питерсом, что добавление отдельного специального значения бесконечности для int может не бытьлучшие идеи.

Однако это затрудняет намеки шрифтов.Предположим, следующий код:

myvar = 10   # type: int
myvar = math.inf  # <-- raises a typing error because math.inf is a float

Однако код ведет себя везде так, как и должен.И мой тип подсказки является правильным везде.

Если я вместо этого напишу следующее:

myvar = 10  # type: Union[int, float]

Я могу назначить math.inf без заминки.Но теперь любой другой float также принят.

Есть ли способ правильно ограничить подсказку типа?Или я вынужден использовать type: ignore каждый раз, когда назначаю бесконечность?

1 Ответ

0 голосов
/ 24 февраля 2019

Супер-ленивое (и, вероятно, неправильное) решение:

Вместо добавления определенного значения класс int можно расширить с помощью подклассов.Этот подход не обходится без ряда подводных камней и проблем, таких как требование обрабатывать значение бесконечности для различных методов __dunder__ (то есть __add__, __mul__, __eq__ и т. П., И все они должныбыть проверенным).Это было бы недопустимым количеством накладных расходов в тех случаях, когда требуется конкретное значение.В таком случае перенос нужного значения с помощью typing.cast сможет лучше указать системе подсказок типа, что конкретное значение (т. Е. inf = cast(int, math.inf)) будет приемлемым для присвоения.

Причина, по которой этот подход неверен, заключается просто в следующем: поскольку назначенное значение выглядит / ощущается точно как некоторое число, некоторые другие пользователи вашего API могут в конечном итоге непреднамеренно использовать его как int, и тогда программа может взорваться, когдаmath.inf (или варианты такого рода).

Аналогия такова: учитывая, что списки имеют элементы, которые проиндексированы с помощью положительных целых чисел, мы ожидаем, что любая функция, которая возвращает индекс для некоторого элемента, будет некоторойположительное целое число, поэтому мы можем использовать его напрямую (я знаю, что в Python это не так, поскольку есть семантика, позволяющая использовать отрицательные значения индекса, но представьте, что мы сейчас работаем с, скажем, C).Скажем, эта функция возвращает первое вхождение соответствующего элемента, но при наличии ошибок возвращает некоторое отрицательное число, которое явно превышает диапазон допустимых значений индекса для некоторого элемента.Это отсутствие защиты от наивного использования возвращаемого значения неизбежно приведет к проблемам, которые должна решить система типов.

По сути, создание суррогатных значений и маркировка, что int будет предлагать нулевое значение,и неизбежно позволять программе автоматически разрешать непредвиденное и неработающее API / поведение из-за неправильного использования.

Не говоря уже о том, что бесконечность не является числом , следовательно, нет *Значение 1025 * может правильно представлять это (учитывая, что int представляет некоторое конечное число по самой своей природе).

В качестве отступления, посмотрите str.index против str.find.У одного из них есть возвращаемое значение, которое определенно нарушает ожидания пользователя (т. Е. Выходит за границы положительного целого числа типа; не будет сказано, что возвращаемое значение может быть недопустимым для контекста, в котором оно может использоваться во время компиляции, что приводит кпотенциальный сбой случайным образом во время выполнения).

Обрамление вопроса / ответа в более правильных терминах:

Учитывая, что проблема действительно в назначении некоторого целого числа, когда существует ставка, и, если ее нет, некоторыедолжен быть создан другой токен, который представляет неограниченность для конкретного варианта использования (это может быть некоторое встроенное значение, такое как NotImplemented или None).Однако, поскольку эти токены также не будут иметь значения int, это означает, что myvar на самом деле потребуется тип, который включает их, и способ применения операции, которая будет делать правильные вещи.

К сожалению, это не так.Непосредственно доступны в Python очень хорошим способом, однако в строго статических типизированных языках, таких как Haskell, более приемлемым решением является использование типа Maybe для определения числового типа , который может приниматьбесконечность .Обратите внимание, что, хотя бесконечность с плавающей запятой также доступна там, она наследует все проблемы чисел с плавающей запятой, что делает это несостоятельным решением (опять же, не используйте для этого inf).

Возвращаясь к Python: в зависимости от свойства присваивания, которое вы на самом деле хотите, это может быть так же просто, как создать класс с конструктором, который может принимать int или None (или NotImplemented), изатем предоставьте метод, который пользователи класса могут использовать фактическое значение.К сожалению, Python не предоставляет продвинутых конструкций, чтобы сделать это элегантно, так что вы неизбежно получите код, управляющий этим, будет разбросан повсюду, или вам придется написать несколько методов, которые обрабатывают любой ввод, как ожидается, и производят требуемый вывод вконкретные способы, которые действительно нужны вашей программе.

К сожалению, намеки на типы на самом деле только поверхностно отражаются на том, что предлагали и решали более продвинутые языки на более фундаментальном уровне.Я предположил, что если нужно программировать на Python, это лучше, чем не иметь его.

...