【问题标题】:Python3 type annotations for type generating function用于类型生成函数的 Python3 类型注释
【发布时间】:2021-11-09 13:33:10
【问题描述】:

我对 python3 中的类型注释感到有些困惑,特别是对于生成生成类型的生成器函数。我认为,具体来说,我的困惑源于typing.Type 的文档。这是我的代码sn-p:

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”实际作用的问题。我编辑了该实现以提供更简短的中肯帖子,但这里是完整的实现。它执行一些类型强制和类型检查:

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

【问题讨论】:

  • 什么是UserList?它是未定义的。
  • 你能举一个类型推断失败的例子吗?注释看起来不错,虽然我没有看到函数的意义。看起来它并没有比list 做的更多。
  • TypeSequence 实际上是做什么的?它似乎与生成器函数的 Python 定义无关,生成器函数使用yield 生成generator 类型的值。
  • @PKCakeout 是否需要使用UserList,而不仅仅是返回list 的子类?那么你的返回类型可能只是Type[List[UserType]]

标签: python python-typing


【解决方案1】:

TLDR:使用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"

为了类型正确,将返回类型注释为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]]:
    ...

【讨论】:

  • 对不起。很长一段时间过去了——过去几周我无法重新访问有问题的代码,但现在我回来了。我不知道为什么您的解决方案得到-1 票。它似乎有效且有意义!我的问题上最初的重复标记也可能是正确的,但我再也看不到那个了。也感谢您的详细解释,实际上Sequence 没有指定它的初始化方式是有道理的!没有想到,这很有意义。不确定我是否在这里坚持使用 UserList,所以在这种情况下我会坚持使用“可调用”解决方案。
猜你喜欢
  • 1970-01-01
  • 2020-08-10
  • 1970-01-01
  • 1970-01-01
  • 2017-10-02
  • 2018-10-27
  • 2023-03-09
  • 2018-07-23
  • 1970-01-01
相关资源
最近更新 更多