通过类型注释抽象 Python 中的类型构造函数

Abstracting over type constructors in Python via type annotations

我想静态地强制执行一个 class return 的方法,一个包装在某种抽象类型中的值,我对此一无所知:

例如给定摘要 class

F = ???    

class ThingF(Generic[F]):
    @abstractmethod
    def action(self) -> F[Foo]:
        ...

我希望能够静态检查这是否无效:

class ThingI(ThingF[List]):
    def action(self) -> Foo:
        ...

因为 action 没有 return List[Foo].

但是 ThingF 的上述声明甚至没有 运行,因为 Generic 期望它的参数是类型变量,而我找不到使 F 的方法类型变量 "with a hole".

两者都

F = TypeVar('F')

T = TypeVar('T')
F = Generic[T]

不起作用,因为 TypeVar 不可下标或 Generic[~T] 不能用作类型变量。

基本上我想要的是 "higher kinded type variable",类型构造函数的抽象,如果您愿意的话。 IE。上面写着 "F can be any type that takes another type to produce a concrete type".

的东西

有什么方法可以用 Python 的类型注释来表达它并用 mypy 进行静态检查吗?

不幸的是,类型系统(如 PEP 484 中所述)不支持更高种类的类型——这里有一些相关的讨论:https://github.com/python/typing/issues/548

mypy 和其他类型检查工具可能会在将来的某个时候获得对它们的支持,但我不会屏住呼吸。这需要一些相当复杂的实施工作才能完成。

您可以将更高种类的类型与 dry-python/returns 一起使用。 我们同时发布原语和自定义 mypy 插件以使其正常工作。

这是一个 Mappable 又名 Functor 的例子:

from typing import Callable, TypeVar

from returns.interfaces.mappable import MappableN
from returns.primitives.hkt import Kinded, KindN, kinded

_FirstType = TypeVar('_FirstType')
_SecondType = TypeVar('_SecondType')
_ThirdType = TypeVar('_ThirdType')
_UpdatedType = TypeVar('_UpdatedType')

_MappableKind = TypeVar('_MappableKind', bound=MappableN)


@kinded
def map_(
    container: KindN[_MappableKind, _FirstType, _SecondType, _ThirdType],
    function: Callable[[_FirstType], _UpdatedType],
) -> KindN[_MappableKind, _UpdatedType, _SecondType, _ThirdType]:
    return container.map(function)

它适用于任何 Mappableexamples:

from returns.maybe import Maybe

def test(arg: float) -> int:
        ...

reveal_type(map_(Maybe.from_value(1.5), test))  # N: Revealed type is 'returns.maybe.Maybe[builtins.int]'

并且:

from returns.result import Result

def test(arg: float) -> int:
    ...

x: Result[float, str]
reveal_type(map_(x, test))  # N: Revealed type is 'returns.result.Result[builtins.int, builtins.str]'

它肯定有一些限制,比如:它只适用于直接的 Kind 子类型,我们需要一个单独的别名 Kind1Kind2Kind3,等。因为当时 mypy 不支持可变泛型。

来源:https://github.com/dry-python/returns/blob/master/returns/primitives/hkt.py 插件:https://github.com/dry-python/returns/blob/master/returns/contrib/mypy/_features/kind.py

文档:https://returns.readthedocs.io/en/latest/pages/hkt.html

公告post:https://sobolevn.me/2020/10/higher-kinded-types-in-python