Как стать автором
Обновить

Комментарии 15

Не совсем понял, о каком формальном параметре идёт речь. Если вы о функции sin, то реальная умеет работать со всеми типами:

>>> sin(True)
0.8414709848078965
>>> sin(Decimal(1))
0.8414709848078965
>>> sin(Fraction(1, 1))
0.8414709848078965


и не типизирована docs.python.org/3/library/math.html#math.sin

Параметр только позиционный

>>> sin(x=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: math.sin() takes no keyword arguments


Наверно, не стоило использовать название реальной ф-ции, но думал, кавычек в тексте хватит.

def sin(x: Number):

Вопрос в том, зачем так было писать. Неважно, как эта функция называется.

Так-то уж, если по существу разбираться, элементарные типы вроде int по своей реализации не являются наследниками абстрактных классов.

Зачем говорить, что ф-ция принимает число?

Я не понимаю, что такое “число”. Это вредная генерализация понятия.

Со времён после Фортрана II договорились иногда разрешать использовать целые константы в качестве вещественных, но нельзя же этот весьма дурно пахнущий трюк эксплуатировать, расширяя до бесконечности.

А, кажется, понял, вы имеете ввиду, что предпочли бы видеть

def f(x: Union[int, float, Decimal, Fraction, Complex])?

Я бы предпочёл видеть f(x). А если уж хочется расписать типы, то тогда надо для начала разбираться, int там должно быть по существу задачи или float.

Бессмысленно специфицировать тип просто исходя из того, что технически в состоянии принять синус в конечном итоге. Это то же самое, что отсутствие спецификации, только запутаннее.

Судя по тому, что баг за 5 лет так и не пофиксили, не вы не одиноки с данным мнением)

А решение-то — единственный кусок кода (псевдокод, ибо питонам не обучен):

def sin(int x)
sin(float::x)
end

Раз требуется, чтобы параметр был определённого типа — пишете обёртку, которая берёт "любой" тип, и приводит его к "определённому".

Честно говоря, не назвал бы это «решение» решением.
В общем случае это не работает — представьте, что «определённым» типом является int, а «любым» — float или complex. Уверены, что есть разумные правила приведения?
Кстати, что насчёт ситуации, когда приведение типа завершилось неудачно?
Спасибо за интерес.

В python приведение типа работает только для анализатора docs.python.org/3/library/typing.html#typing.cast

Кроме того, оно приводит любой тип к любому

from typing import cast


def s(x: str):
    pass


s(cast(str, 1))
# Success: no issues found in 1 source file


Что равнозначно отсутствию проверки типов совсем.

Но как таковой проблемы нет, случаев, когда в одну ф-цию захочется передавать все эти типы очень немного, ничто не мешает в таких общих случаях тип не декларировать или перечислять все базовые типы:

def f(x: Union[float, Decimal])?


Или объявить протокол:

from decimal import Decimal
from typing import cast, Protocol


class MyNumber(Protocol):
    def __add__(self, other):
        pass

    def __mul__(self, other):
        pass

    def __pow__(self, power, modulo=None):
        pass


def s(x: MyNumber):
    pass


s(cast(str, 1))
s(Decimal(1))


Строка не пройдёт:
ws.py:21: error: Argument 1 to "s" has incompatible type "str"; expected "MyNumber"
ws.py:21: note: 'str' is missing following 'MyNumber' protocol member:
ws.py:21: note:     __pow__
Found 1 error in 1 file (checked 1 source file)


Правда, не пройдёт только из-за того, что у неё не определёна операция возведения в степень.

Если бы вы сначала разобрались, а не побежали бы поскорее писать статью на Хабр, вы бы написали статью совсем о другом, другой длины и с нормальным разбором конкретной темы. В текущем варианте вы прыгаете с одного вопроса на другой и смешиваете две разных системы.

Для начала стоило бы в статье оставить ссылку на само обсуждение.

Так называемый number (или numeric) tower изначально определён как модуль numbers в PEP 3141 от далёкого 2007 года. То есть за год до релиза Python 3 и за семь лет до появления тайпхинтов в 2014. Тот же самый PEP 484, прямо в том же абзаце, который вы привели в пример, предлагает использовать реальные классы для реализации numeric tower, а вы это предложение игнорируете и продолжаете использовать старые абстрактные классы.

Более того, давайте посмотрим в тот самый PEP 3141 и узнаем, как должен быть определён Number:

class Number(metaclass=ABCMeta): pass

То есть, в общем-то, никаких методов нам этот класс не даёт (потому что это протокол для абстрактного числа, а число может иметь совершенно разные свойства и разные операции — к примеру, если вы принимаете два числа, вы не можете их сравнивать, потому что сравнение не определено для комплексных чисел). Что вы имеете в виду, когда пишете x: Number?

Давайте и дальше читать половину документации и бежать оформлять текст на Хабр из-за этого.

Спасибо за интерес к статье.

Статья написана в пятницу и представляет собой обычную не очень серьёзную пятничную хабра статью. Не стоит относиться к ней как к статье по теории типов в Oxford University Press или IEEE.

P.S. Ссылку забыл добавить, сейчас пофикшу, спасибо.

Да я не говорю, что статья должна тянуть на серьёзное исследование, просто она какая-то пустая, что ли. Я за 5 минут пролистал обсуждение и пробежался по PEP 484/3141 достаточно, чтобы расписать суть лучше.

Хотелось бы, чтобы авторы статей на Хабре тоже разбирались в предмете статьи, хотя бы в виде 10-минутного прочтения литературы об этом.

В питоне реально приятно работать. Не смотря на некоторые проблемы, привлекает своей универсальностью и не особо сложный. Но Ява всё же поинтереснее будет

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации