Проблемы с «typing.ClassVar» при использовании фабричной функции для генерации типов аннотаций в Python 3.7 - PullRequest
0 голосов
/ 10 сентября 2018

Я пытаюсь использовать фабричную функцию для генерации аннотаций некоторых типов - особенно для tuple типов. У меня есть одна версия фабрики, которая работает нормально (например, она компилируется, запускается и проверяется удовлетворительно в MyPy):

import typing as tx
HomogenousTypeVar = tx.TypeVar('HomogenousTypeVar')
TupleTypeReturnType = tx.Type[tx.Tuple[HomogenousTypeVar, ...]]

def TupleType(length: int,
              tuptyp: tx.Type[HomogenousTypeVar] = str) -> TupleTypeReturnType:
    """ Create a type annotation for a tuple of a given type and length """
    assert length > 0
    return tx.Tuple[tuple(tuptyp for idx in range(length))]

… для которого использование, например, например:

class Thing(object):

    __slots__: TupleType(2) = ('yo', 'dogg')
    other_fields: TupleType(4) = ('i', 'heard',
                                  'you', 'like')

    # etc, or what have you

… однако, я потерпел неудачу, когда попытался добавить поддержку для аннотации typing.ClassVar, которая выглядела так:

import typing as tx
HomogenousTypeVar = tx.TypeVar('HomogenousTypeVar')
TupleTypeReturnType = tx.Union[tx.Type[tx.Tuple[HomogenousTypeVar, ...]],
                               tx.Type[tx.ClassVar[tx.Tuple[HomogenousTypeVar, ...]]]]

def TupleType(length: int,
              tuptyp: tx.Type[HomogenousTypeVar] = str,
              clsvar: bool = False) -> TupleTypeReturnType:
    """ Create a type annotation for a tuple of a given type and length,
        specifying additionally whether or not it is a ClassVar """
    assert length > 0
    out = tx.Tuple[tuple(tuptyp for idx in range(length))]
    return clsvar and tx.ClassVar[out] or out

… после этого изменения код даже не будет изначально скомпилирован - он не может это сделать с помощью TypeError из глубины модуля typing:

TypeError: typing.ClassVar [typing.Tuple [~ HomogenousTypeVar, ...]] is недопустимо в качестве аргумента типа

… что, как ни странно, звучит как ошибка; Я имею в виду, разве все в typing не должно быть в некотором роде допустимым аргументом типа, дай или возьми?

В исходном коде typing, относящемся к ClassVar, есть несколько ограничений на его использование, упомянутых в строке документации, но это не один из них. Есть что-то очевидное, чего мне не хватает? Является ли моя попытка использовать эту аннотацию подобным образом? Что еще я могу попробовать?

1 Ответ

0 голосов
/ 11 сентября 2018

Вы уверены, что ваш оригинальный фрагмент кода на самом деле проверяет тип с помощью mypy?Когда я пытаюсь запустить его, используя Mypy 0.620 или последнюю версию от github, я получаю следующие ошибки:

test.py:13: error: invalid type comment or annotation
test.py:13: note: Suggestion: use TupleType[...] instead of TupleType(...)
test.py:14: error: invalid type comment or annotation
test.py:14: note: Suggestion: use TupleType[...] instead of TupleType(...)

Я также не могу воспроизвести ошибку, которую вы получаете с вашим кодом ClassVar - когдаЯ пытаюсь запустить его, я получаю следующие ошибки:

test.py:4: error: Invalid type: ClassVar nested inside other type
test.py:6: error: Incompatible default for argument "tuptyp" (default has type "Type[str]", argument has type "Type[HomogenousTypeVar]")
test.py:12: error: Invalid type alias
test.py:13: warning: Returning Any from function declared to return "Union[Type[Tuple[HomogenousTypeVar?, ...]], Type[Tuple[HomogenousTypeVar?, ...]]]"
test.py:15: error: Name 'Thing' is not defined
test.py:16: error: Revealed type is 'Any'

Вы уверены, что на самом деле вы используете mypy, а не просто код?Например, если вы запускаете только python3 test.py, вы в основном пропускаете все проверки типов (за исключением некоторых проверок минимальной разумности, встроенных в модуль ввода).

Если вы хотите проверить код, введите pip-install mypy и запустите python3 -m mypy test.py.


В любом случае все эти сообщения об ошибках ожидаемого поведения- mypy (и любая другая совместимая с PEP 484 программа проверки типов) может только анализировать ваш код статически и не будет пытаться запускать или анализировать какие-либо фабричные функции / любые функции генерации подсказок типа, которые вы можете попытаться написать.

Таким образом, это означает, что, к сожалению, вы не сможете использовать сгенерированную подсказку типа с ClassVars, если вы хотите, чтобы PEP 484-совместимые инструменты могли анализировать ваш код - они не могут понять / интерпретировать ваш оригиналнабор подсказок типов, и добавление ClassVars определенно не поможет.

Если вы хотите генерировать подсказки типов, единственная реальная возможность, о которой я могу подумать, - это создать какой-нибудь мини-язык или систему макросов поверхPython, который при запуске будет генерировать код Python.Затем вы запустили бы и проверили сгенерированный код вместо вашего макрофилированного языка Python.

Но я действительно не рекомендую делать это - это очень хрупкий взлом.


В более широком смыслевсякий раз, когда вы начинаете сталкиваться с подобными ограничениями, связанными с типами, я думаю, это признак того, что ваш код слишком сложен.Я бы хотел либо упростить ваш код, либо (если это невозможно) переключиться на такой язык, как Haskell или Idris, который позволил бы вам использовать более выразительную (хотя и более сложную) систему типов.

Например, в этомВ этом случае вы пытаетесь обобщить тип Tuple - это заставляет меня сделать вывод, что ваша кодовая база содержит множество различных типов кортежей разных типов и типов.

Это кажется мне немного подозрительным - явместо этого мы будем рассматривать преобразование некоторых из этих кортежей в обычные классы или (если вам по-прежнему нужна функциональность, подобная кортежу) в namedtuple . Классы данных (которые являются новыми с Python 3.7 ) также могут быть удобны здесь.

Эти решения также помогут сделать ваш код немного более читабельным - вы можетеТеперь дайте конкретные имена и значения каждому отдельному типу кортежей.

В качестве альтернативы, если у вас есть только несколько разных типов кортежей, но вы используете эти кортежи повсюду, вы можете попробовать использовать псевдонимы типов , поэтому вам не придется многократно набирать один и тот же (длинный) тип снова и снова.Например, вместо:

def foo(x: Tuple[int, int, int, int]) -> None: ...

... вы можете сделать:

IpAddress = Tuple[int, int, int, int]

def foo(x: IpAddress) -> None: ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...