3

我为有序集贡献了类型提示。问题是,尽管我在文件中有以下几行:ordered_set.pyi

from typing import MutableSet, TypeVar, Sequence

T = TypeVar('T')

class OrderedSet(MutableSet[T], Sequence[T]):
    ...

我不能写:

IntOrderedSet = OrderedSet[int]

在我的代码中,Python 提出:

TypeError: 'ABCMeta' object is not subscriptable

发生这种情况是因为 inordered_set.py OrderedSet定义为:

from collections.abc import MutableSet, Sequence

class OrderedSet(MutableSet, Sequence):
    ...

我提交了一个 PR,将OrderedSetclass更改为从键入 classes 继承,但ordered-set所有者拒绝接受它,因为正如他所说:

在代码中导入类型会增加代码的安装时依赖性和运行时成本。ordered_set 作为一个没有依赖关系的单个模块已经 6 年了。有人可能决定他们不想与 setuptools 有任何关系,只需将 ordered_set 放到他们的 PYTHONPATH 上,它仍然可以工作。我不想在类型上失去它。

有没有办法在OrderedSet[int]不修改库ordered_set.py文件的情况下支持类似的类型?

4

1 回答 1

4

我知道的唯一解决方法是定义任何自定义类型别名,以便它们仅在类型检查时存在,而从不在运行时存在。

为此,您需要:

  1. if typing.TYPE_CHECKING:在块内定义任何类型别名。该typing.TYPE_CHECKING块在运行时始终为假,并被类型检查器视为真。
  2. 确保您的所有类型提示都是字符串——或者,如果您使用的是 Python 3.7,请添加from __future__ import annotations,它会自动执行此操作。也就是说,首先要避免评估类型提示。

因此,例如,在您的特定设置中,您可以在某处为 OrderedSet 库贡献存根,然后将代码编写成如下所示(假设 Python 3.7+):

from __future__ import annotations
from typing import TYPE_CHECKING
from ordered_set import OrderedSet

# Basically equivalent to `if False`
if TYPE_CHECKING:
    IntOrderedSet = OrderedSet[Int]

def expects_int_ordered_set(x: IntOrderedSet) -> None:
    # blah

some_ordered_set: IntOrderedSet = OrderedSet()

或者,如果您使用的是 Python 3.6 或更低版本:

from typing import TYPE_CHECKING
from ordered_set import OrderedSet

if TYPE_CHECKING:
    IntOrderedSet = OrderedSet[Int]

def expects_int_ordered_set(x: 'IntOrderedSet') -> None:
    # blah

some_ordered_set: 'IntOrderedSet' = OrderedSet()

如果你可以稍微撒谎,我们可以省去字符串的东西,并IntOrderedSet在类型检查时与运行时定义稍微不同的东西。例如:

from typing import TYPE_CHECKING
from ordered_set import OrderedSet

if TYPE_CHECKING:
    IntOrderedSet = OrderedSet[Int]
else:
    IntOrderedSet = OrderedSet

def expects_int_ordered_set(x: IntOrderedSet) -> None:
    # blah

some_ordered_set = IntOrderedSet()

不过,请确保在执行此操作时要小心——类型检查器不会检查“else”块中的任何内容/不会检查以确保您在那里所做的任何事情与该if TYPE_CHECKING块中的内容一致。

最终的解决方案是首先不定义IntOrderedSet类型。这让我们可以跳过 hack 1,而只需要使用 hack 2(这并不是一个 hack——几年后它将成为 Python 中的默认行为)。

例如,我们可以这样做:

from __future__ import annotations
from ordered_set import OrderedSet

def expects_int_ordered_set(x: OrderedSet[int]) -> None:
    # blah

some_ordered_set: OrderedSet[int] = OrderedSet()

根据上下文,可能我什至不需要在最后一个变量声明中添加注释。在这里,我们这样做了,但是如果我们在函数内部定义该变量,则像 mypy 这样的类型检查器可能会根据我们最终使用该变量的方式自动为我们推断出正确的类型。

于 2018-09-29T16:36:28.420 回答