Python3 类型生成函数的类型注释

Python3 type annotations for type generating function

我对 python3 中的类型注释有点困惑,特别是对于吐出生成类型的生成器函数。我认为,具体来说,我的困惑源于 typing.Type 的文档。这是我的代码片段:

from collections import UserList
UserType = TypeVar('UserType')
def TypeSequence(usertype: Type[UserType]) -> Type[Sequence[UserType]]:
    class Result(UserList):
        ... # Cut out the implementation for brevity's sake
    return Result

生成的“TypeSequence”正在做一些类型检查,以便只生成可序列化的数据结构,这对这个问题并不重要。关键是你应该能够做这样的事情:

MyIntSequence = TypeSequence(int)
MyIntSequence((1, 2, 3)) -> [1, 2, 3] with type Sequence[Int]


MyTupleSequence = TypeSequence(tuple)
MyTupleSequence(((1, 2), (3, 4))) -> [(1, 2), (3, 4)] with type Sequence[tuple]

我的问题:我提供的类型注释是否正确?

我的疑问主要来自 PyCharm,它未能提供由我的自定义生成器函数生成的类型。 PyCharm 可能是个问题,但我对此表示怀疑,因为它对于标准库非常有效,标准库几乎使用同样复杂的类型注释。


类型推断似乎失败的简单示例:

请注意这与此列表版本的对比:


我也收到很多关于“TypeSequence”的实际作用的问题。我对该实现进行了编辑,使其更简洁 post,但这里是完整的实现。它执行一些类型强制和类型检查:

from collections import UserList
from typing import (Optional, Any, Sequence, Callable, Hashable, Dict, Mapping, Type, TypeVar,
)


UserType = TypeVar('UserType')
def TypeSequence(usertype: Type[UserType]) -> Type[Sequence[UserType]]:
    class Result(UserList):
        def __init__(self, *args):
            from collections import Iterable
            if len(args) == 0:
                super(Result, self).__init__()
            elif len(args) == 1:
                if not isinstance(args[0], Iterable):
                    raise ValueError("Not a iterable")
                if issubclass(usertype, tuple) and hasattr(usertype, "_fields"):
                    if any(not isinstance(x, Iterable) for x in args[0]):
                        raise ValueError("Invalid initializer for named tuple")
                    if len(args[0]) != len(usertype._fields):
                        raise ValueError(f"Not enough values to initialize {usertype}")
                    seq = (usertype(*x) for x in args[0])
                else:
                    seq = (usertype(x) for x in args[0])
                super(Result, self).__init__(seq)

    Result.__name__ = f"TypeSequence[{usertype.__name__}]"

    return Result

TLDR:对于要实例化的任何类型,使用 Callable 而不是 Type。具体来说,明确 return 类型签名。

def TypeSequence(
    usertype: Type[UserType]
) -> Callable[[Iterable[UserType]], Sequence[UserType]]
    ...

A Type[Sequence[UserType]] 无法实例化,因为 Sequence 是抽象类型。 mypy 将实例化标记为无效:

XSeq = TypeSequence(X)
x_seq = XSeq([X()])  # error: Too many arguments for "Sequence"

为了类型正确,请将 return 类型注释为 ListUserList

def TypeSequence(usertype: Type[UserType]) -> Type[UserList[UserType]]:
    ...

除了类型正确性之外,请注意 PyCharm 通常无法理解复杂的 Type 关系。揭示函数的类型表明 Type[UserList[UserType]] 被简化为 Type[UserList].

使用 Callable 代替允许表达复杂类型实例化。可以定义精确的签名,包括 Sequence 而不是 UserList:

def TypeSequence(usertype: Type[UserType]) -> Callable[[Iterable[UserType]], Sequence[UserType]]:
    ...