Python декоратор для проверки типа возвращаемого значения: особенно проверяйте наличие вложенных вещей, таких как List [List [List [str]]] - PullRequest
1 голос
/ 05 августа 2020

Пытаюсь написать свой единственный python декоратор для проверки типов. Он работает хорошо, но борется с подсказками вложенного типа. Например, возьмите

@type_check
def fun(x: int) -> List[List[List[str]]]:
    return [[[str(x)]]]

fun(x=42)

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

import inspect
# inside the decorator
actual_result = func(*args, **kwargs)              # [[['42']]]
expected_result_type = spec.annotations['return']  # typing.List[typing.List[typing.List[str]]]

И теперь задача проверить: [[['42']]] имеет тип typing.List[typing.List[typing.List[str]]]. Как я могу это сделать?

Все, что я узнал, это то, что [[['42']]] имеет просто тип <class 'list'>, который полностью игнорирует информацию о вложении. И я могу проверить это с помощью

if hasattr(expected_result_type, '__origin__'):
    expected_result_type = expected_result_type.__origin__

if expected_result_type is not None:
    assert isinstance(result, expected_result_type)
else: # None is kind of a special case
    assert result is expected_result_type

Это работает на «внешнем слое», но игнорирует подсказки вложенного типа. Есть ли возможность проверить все слои?

1 Ответ

3 голосов
/ 05 августа 2020

Простой ответ:

Просто введите все аргументы, например:

def type_check(value, annotation):
    if isinstance(annotation, type):
        return isinstance(value, annotation)
    elif annotation == typing.T or annotation == typing.Any:
        return True
    elif isinstance(annotation, typing._GenericAlias):
        if annotation.__origin__ == list:
            if not isinstance(value, list):
                return False
            inner_annotation = annotation.__args__[0]
            return all(type_check(val, inner_annotation) for val in value)

Пример

>>> type_check(5, int)
True
>>> type_check([1, 2, 3], typing.List[int])
True
>>> type_check([1, 2, 3], typing.List[float])
False

Который вы можете просто использовать в своем декораторе. Его можно улучшить для обработки Dict, Tuple и других распространенных типов.

Однако это просто игрушечный пример, если вы хотите выполнить надежную динамическую проверку типов c, вам придется обрабатывать Generator, Callable, Iterator et c ... что не только довольно сложно, но и в некоторых случаях совершенно невозможно (например, итераторы не могут быть проверены без повторения).

Посмотрите в проекте enforce , что и есть: проверка типов python Dynami c с использованием декораторов. Исходный код модуля typing тоже очень интересно читать, но он действительно расширяет пределы возможностей динамической c интроспекции.

Надеюсь, это поможет;)

...