Python - типизация - объединение подписного типа - PullRequest
0 голосов
/ 14 декабря 2018

Я хотел бы создать тип Array, который должен иметь подписку и быть объединением типов typing.List и numpy.ndarray.Я знаю, что numpy не поставляется с заглушками, но эти тупые заглушки ( Machinalis ) должны нормально работать, так как они являются подподлинными.

Это ожидаемоеПоведение:

def foo(bar: Array[int])->None:
    pass

foo([1,2,3])          # No typing error
foo(numpy.arange(4))  # No typing error
foo((1,2,3))          # Error: Expected Array[int], got Tuple[int]
foo([1.,2.,3.])       # Error: Expected Array[int], got Array[float]

Я пробовал несколько вещей, но ни одна из них не работала так, как ожидалось.

Как бы вы это сделали в Python 3.7 ?

Я также приму какое-нибудь решение по типизации уток, даже если оно не удовлетворяет ошибке Tuple.Основной целью является создание подписного объединения подписных типов.

Спасибо.

Моя лучшая попытка: (ошибки mypy в комментариях)

class _meta_getitem(type):
    def __getitem__(cls, x):
        return cls.__getitem__(cls, x)

class Array(metaclass=_meta_getitem):

    def __getitem__(self, element_type: type) -> type:
        array_type = typing.Union[List[element_type],  # error: Invalid type "element_type"
                                  numpy.ndarray[element_type]]
        return typing.NewType("Array[{}]".format(element_type.__name__), 
                              array_type)  # The type alias to Union is invalid in runtime context

if __name__ == "__name__":
    x: Array[int] = numpy.arange(4) # "Array" expects no type arguments, but 1 given

1 Ответ

0 голосов
/ 14 декабря 2018

Создание псевдонима типа Union[List[T], Array[T]] должно работать:

from typing import TypeVar, Union, List

T = TypeVar('T')
Array = Union[List[T], numpy.ndarray[T]]

def foo(bar: Array[int]) -> None: pass

См. Документы mypy по псевдонимам универсального типа для получения дополнительной информации об этой технике.

Этот код может потенциально потерпеть неудачу во время выполнения, так как numpy.ndarray на самом деле не является подписанным во время выполнения, только в мире подсказок типов.Вы можете обойти это, спрятав свой собственный подсказка типа за защитой typing.TYPE_CHECKING, которая всегда ложна во время выполнения и истинна во время проверки типа.

Вы можете сделать это относительно чисто в Python 3.7 +:

from __future__ import annotations
from typing import TypeVar, Union, List, TYPE_CHECKING

if TYPE_CHECKING:
    T = TypeVar('T')
    Array = Union[List[T], numpy.ndarray[T]]

def foo(bar: Array[int]) -> None: pass

Вы должны заключить Array[int] в строку для более старых версий Python 3, однако:

from typing import TypeVar, Union, List, TYPE_CHECKING

if TYPE_CHECKING:
    T = TypeVar('T')
    Array = Union[List[T], numpy.ndarray[T]]

def foo(bar: "Array[int]") -> None: pass

Обратите внимание, что при попытке создать свой собственный Array подсказка типа путем объединения нескольких других подсказок типа во время выполнения вряд ли сработает: инструменты статического анализа, такие как mypy, работают, фактически анализируя ваш код без его запуска: на самом деле он не собирается пытаться что-либо оценить внутри вашего пользовательского Array класса.

В общем, попытка «использовать» подсказки типов во время выполнения может быть сопряжена с опасностью: на самом деле они предназначены только для использования в качестве подсказок типа.

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

...