用于类型生成功能的Python3类型注释

时间:2020-03-25 10:43:11

标签: python python-typing

对于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可能是一个问题,但我对此表示怀疑,因为它对于标准库非常有效,该标准库几乎使用同样复杂的类型注释。


一个简单的例子,其中类型推断似乎失败了:

Example of failing syntax highlighting

请注意,此列表版本与此相反:

Example of working syntax highlighting


我也对“ TypeSequence”实际上做什么有很多疑问。我对该实现进行了重新编写,以使内容更简短,但是这里是完整的实现。它执行一些类型强制和类型检查:

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

我认为这不会增加任何问题,但是您可以:D

1 个答案:

答案 0 :(得分:1)

TLDR:对于要实例化的任何类型,请使用# UNTESTED. Note that TRUNCATE is IRREVERSIBLE. Invoke-SqlCmd -Query "TRUNCATE TABLE $SQLSchema.$SQLTable" ` -ServerInstance $SQLServer -Database $SQLDatabase -Credential $SQLCredential -Force 而不是Callable。具体来说,要明确说明返回类型签名。

Type

def TypeSequence( usertype: Type[UserType] ) -> Callable[[Iterable[UserType]], Sequence[UserType]] ... 是抽象类型,因此无法实例化Type[Sequence[UserType]]Sequence将实例化标记为无效:

mypy

为正确起见,请将返回类型注释为XSeq = TypeSequence(X) x_seq = XSeq([X()]) # error: Too many arguments for "Sequence" List

UserList

除了类型正确性之外,请注意,PyCharm通常不理解复杂的def TypeSequence(usertype: Type[UserType]) -> Type[UserList[UserType]]: ... 关系。显示函数的类型表明Type简化为Type[UserList[UserType]]

使用Type[UserList]可以表示复杂类型的实例化。可以定义精确的签名,包括Callable而不是Sequence

UserList