Есть ли способ написать собственный метод утверждения, распознаваемый средствами проверки типов?

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

Казалось бы, это легко сделать, но средства проверки статических типов, такие как PyLance, не считают функцию встроенной assert. Рассмотрим этот минимальный пример:

def my_assert(condition: bool, msg: str):
    if not condition:
        logger.error(msg)
        raise AssertionError(msg)

Тогда я ожидал бы, что смогу использовать этот метод как встроенный assert, не раздражаясь тем, что «Оператор '+' не поддерживается для типа 'Нет'».

def add_one(a: int | None) -> int:
    my_assert(a is not None, "a is None")
    return a + 1  # Pylance lints this as being possibly None

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

Что, если вы выполнили вход в систему try...except AssertionError верхнего уровня, а не во вспомогательную функцию нижнего уровня? Тогда внутренний код может использовать обычный старый assert со всеми предоставляемыми удобствами.

Samwise 18.06.2024 22:01
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
79
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По сути, вы ищете однострочник (т. е. простой оператор), который выполняет сужение типа для типа.

Python допускает только определяемые пользователем обратные вызовы, сужающие тип обратные вызовы формата

Callable[[argument to narrow type of, <any other arguments> ...], <narrowed type>]

и сужение типов может происходить только в контексте вызова с помощью assert или if...elif...else-проверок. Итак, вам придется вписать в этот формат свою собственную функцию log-then-assert. На not Nones (Pyright Playground) будет работать что-то вроде следующего:

import typing as t
from logging import getLogger

T = t.TypeVar("T")
logger = getLogger()

def check_type(arg: T | None, condition: bool, msg: str) -> t.TypeGuard[T]:
    if not condition:
        logger.error(msg)
        raise AssertionError(msg)
    return True
def add_one(a: int | None) -> int:
    assert check_type(a, a is not None, "a is None")
    reveal_type(a)  # int
    return a + 1

Я считаю, что в более общих случаях вам придется связать нежелательные типы с классом. Примерно так (Pyright Playground):

Other = t.TypeVar("Other")

class not_(t.Generic[Other]):
    @classmethod
    def check_type(cls, arg: T | Other, condition: bool, msg: str) -> t.TypeGuard[T]:
        if not condition:
            logger.error(msg)
            raise AssertionError(msg)
        return True
def add_one(a: int | str) -> int:
    assert not_[str].check_type(a, type(a) is not str, "a is str")
    reveal_type(a)  # int
    return a + 1

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

Risitop 19.06.2024 08:49

Другие вопросы по теме