Хранить и утверждать тип в классе Python - PullRequest
2 голосов
/ 10 октября 2019

PyCharm CE 2019.2.3 предупреждает меня о классе, в котором я храню list данных вместе со значением data type. Я попытался свести это к сути:

class Data:
    def __init__(self, length, data_type):
        assert isinstance(length, int) and length >= 0
        assert isinstance(data_type, type)  # commenting this line out removes the warning
        self._data = [None] * length
        self._data_type = data_type

    def set_field(self, index, value):
        assert isinstance(value, self._data_type)  # commenting this line out removes the warning
        assert isinstance(index, int)
        self._data[index] = value

Я получаю предупреждение по индексу:

enter image description here

Неожиданный тип (ы):
(int, type)

Возможные типы:
(int, None)
(фрагмент, итерируемый [None])

ПроверкаИнформация: эта проверка обнаруживает ошибки типа в выражениях вызовов функций. Из-за динамического распределения и типизации утки это возможно в ограниченном, но полезном количестве случаев. Типы параметров функции могут быть указаны в строках документации или в аннотации функции Python 3.

Дополнительные комментарии:

  • удаление хотя бы одного из двух отмеченных утверждений удаляетпредупреждение

  • довольно странно, что предупреждение появляется для index, в то время как эти утверждения относятся к value

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

Мой вопрос: Я что-то не так делаю с этим хранением и утверждением типов? Есть ли причина для этого предупреждения, и если нет, то видите ли вы удобный способ его устранения?

1 Ответ

2 голосов
/ 13 октября 2019

Проблема здесь двоякая:

  • PyCharm имеет немного слепое пятно (на момент написания) в отношении правильной обработки метаклассов с помощью ввода. Поэтому без дополнительных «подсказок» он не может вывести правильный тип для value в настоящее время.
  • Поскольку вы инициализировали свой список с помощью None PyCharm обрабатывает его как список, содержащий Nones.

Давайте начнем с проблемы метакласса:

Вы проверяете, является ли data_type классом, проверяя, является ли он экземпляром type (метакласс классов по умолчанию). Затем вы проверяете, является ли value экземпляром класса. Это нормально.

Однако PyCharm предполагает, что ваш data_type является type (что правильно), но после isinstance(value, self._data_type) он также предполагает, что value является type (что неправильно -он должен быть типа _data_type). Так что просто используя assert isinstance(...) с data_type и value PyCharm не сможет определить правильный тип value!

Так что это, вероятно, квалифицируется как ошибка или отсутствующая функция в PyCharm - илив любой библиотеке, используемой PyCharm для определения типов.


Вторая проблема заключается в том, что при инициализации _data с помощью None PyCharm выведет тип _data как List[None] (aсписок, содержащий Nones).

Так что, если PyCharm выводит что-либо, кроме Any (который может быть назначен на что угодно) или None (который будет ожидаемым типом содержимого списка) для value, это будетвыдает предупреждение.

Даже при:

    def set_field(self, index, value):
        assert isinstance(value, int)  # <-- difference
        assert isinstance(index, int)
        self._data[index] = value

Придет предупреждение.


На данный момент у вас есть два варианта:

  • Игнорировать предупреждение.
  • Использовать полноценные подсказки типов (если целевые версии Python допускают это).

Если вы хотите использовать подсказку по типу, вы можете, например, использовать:

from typing import List, Optional, TypeVar, Generic, Type

T = TypeVar('T')

class Data(Generic[T]):

    _data: List[Optional[T]]
    _data_type: Type[T]

    def __init__(self, length: int, data_type: Type[T]):
        self._data = [None] * length
        self._data_type = data_type

    def set_field(self, index: int, value: T):
        if isinstance(value, self._data_type):
            self._data[index] = value
        raise TypeError()

Примечание: я полностью удалил утверждения. Если аргументы не имеют ожидаемого типа или значения, TypeError или ValueError будут более информативными. Однако в большинстве случаев документация и / или подсказки типов могут адекватно заменить assert s, а также TypeError s в Python (по крайней мере, на IDE или при использовании mypy).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...