我想创建一个Array
类型,该类型应该可订阅,并且是typing.List
和numpy.ndarray
类型的并集。
我知道numpy
并没有存根,但是those numpy stubs(由 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
答案 0 :(得分:2)
创建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
有关此技术的更多信息,请参见generic type aliases上的mypy文档。
此代码可能在运行时失败,因为numpy.ndarray
实际上不是在运行时可被下标的,仅在类型提示世界中才可以使用。您可以通过在typing.TYPE_CHECKING
保护后面隐藏自定义类型提示来解决此问题,该提示在运行时始终为false,在类型检查时始终为true。
您可以在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
对于旧版本的Python 3,您必须将Array[int]
包装在字符串中,
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
的用途。我建议阅读the relevant docs for more info。