Python 输入:return 使用 Clazz[T] 等泛型输入 Java Clazz<T>

Python typing: return type with generics like Clazz[T] as in Java Clazz<T>

所以我知道 Python 的 typing.Optional。但是我自己写了粗略的PyOptional(代码here),想把Optional[T]和我的PyOptional合并到PyOptional[T]

我目前正在使用 Python 3.7 并尝试扩展 typing.Optional

我的一些 PyOptional:

class PyOptional:
    T: TypeVar = TypeVar("T")

    def __init__(self, obj: T):
        self.value: Any = obj

    def get(self) -> Optional[T]:
        return self.value

    def or_else(self, default) -> T:
        return self.value or default

我想要的伪代码:

def find_user_by_id(id: int) -> PyOptional[User]:
    return PyOptional(db.find_user_by_id(id))

目标是让我的 IDE 能够检查预期的 return 类型并且仍然能够在 returned 对象上调用我的方法。所以它必须符合 PEP。

您应该查看有关泛型的文档 -- 具体来说,user-defined generics. The mypy docs also have a thorough overview of generics 可以作为参考。

在这种特殊情况下,您希望通过添加 Generic[T] 作为 class 基础来使整个 class 通用。仅在单独的函数签名中使用 T 将使每个 单独的 函数通用,但不是整个 class:

from typing import TypeVar, Generic, Optional

T = TypeVar("T")

class PyOptional(Generic[T]):
    def __init__(self, obj: Optional[T]) -> None:
        self.value = obj

    def get(self) -> Optional[T]:
        return self.value

    def or_else(self, default: T) -> T:
        return self.value or default

一些补充说明:

  1. 不要为任何 TypeVar 变量添加注释。在这里,T 是一种元类型构造,用作“孔”/可以表示任意数量的类型。所以,给它分配一个固定的类型并没有多大意义,而且会混淆类型检查器。

  2. 永远不要在任何给定的签名中只使用一次 TypeVar——使用 TypeVars 的全部意义在于你可以声明两个或多个类型总是相同的。

    注意上面固定的PyOptionalclass也遵守这个规则。例如,取 get。现在我们将整个 class 变成了泛型,这个函数的类型签名现在基本上类似于 def get(self: PyOptional[T]) -> Optional[T]。以前更像是def get(self: PyOptional) -> Optional[T].

  3. 为了使 class 有意义,您可能希望构造函数接受 Optional[T] 而不仅仅是 T.

  4. 使 self.value Any 可能 unnecessary/is 过于含糊。我们可以省略类型提示,现在它的推断类型为 Optional[T].

  5. 如果您想更彻底地检查您的 class 是否符合 PEP 484 并且可能会被 IDE 理解,例如 PyCharm,考虑通过 mypy 使用 class 对 class + 一些代码进行类型检查,PEP 484 类型检查器。

    这不能保证您的 IDE 会完全理解您的 class(因为它可能无法完全实现关于 PEP 484 的所有内容/您可能 运行 mypy 或你的 IDE),但它应该可以帮助你接近。