请考虑以下事项:

from typing import TypeVar, Generic

class A(): pass
class B(A): pass
class C(A): pass


T = TypeVar("T", bound=A)


class Container(Generic[T]):
    def __init__(x: T):
        pass

b: Container[B] = Container(B())
c: Container[C] = Container(C())

如何输入将接受相同类型容器的任何元组列表的函数foo:

foo([
    (b, b), 
    (c, c)
    ])

但是拒绝这个(异类元组):

foo([
    (c, b),  # tuples elements are not the same type
    (c, c)
    ])

我试过这个:

ListOfSameContainerTuples = list[tuple[Container[T], Container[T]]]

def foo(containers: ListOfSameContainerTuples[T]):
    pass

但它不起作用:

foo([
    (b, b), 
    (c, c)
    ])  
# error: Argument 1 to "foo" has incompatible type "list[tuple[object, object]]"; 
# expected "list[tuple[Container[<nothing>], Container[<nothing>]]]"  [arg-type]

foo([
    (c, b), 
    (c, c)
    ])
# error: List item 0 has incompatible type "tuple[Container[C], Container[B]]"; expected "tuple[Container[C], Container[C]]"  [list-item]

关于mypy的第二个错误看起来不错,但我不理解第一个:

  • 为什么Mypy不知道bc的类型?
  • 为什么我的类型签名是Container[<nothing>]

我try 了Tuple with multiple numbers of arbitrary but equal type种解决方案,但无济于事.

推荐答案

作为部分解决方案*,您可以将TypeVar T covariant:

T = TypeVar("T", bound=A, covariant=True)

然后

foo([(b, b), (c, c)])

mypy --strict通过类型判断,而

foo([(c, b), (c, c)])

失败并显示以下消息

error: Cannot infer type argument 1 of "foo"

*这是可行的,因为当T协变时,类型tuple[Container[A], Container[A]]被认为是tuple[Container[B], Container[B]]tuple[Container[C], Container[C]]的超类型.在使T协变之前,您的类型判断器推断出了表达式

[(b, b), (c, c)]

因为objecttuple[Container[B], Container[B]]tuple[Container[C], Container[C]]共有的唯一超型.在使T协变之后,该表达式可以改为被推断为具有类型tuple[Container[A], Container[A]],该类型可以通过取值T=Afoo的参数签名绑定.

混合型 case 失败的原因稍微复杂一些,仍然有一些错误的漏洞.在设置为T协变后,您的类型判断器会推断出表达式

(c, b), (c, c)

作为有类型的

list[tuple[Container[C], Container[A]]]

这不能与第一个参数foo绑定,因为即使Container[A]Container[C]tuple[Container[A], Container[A]]is nottuple[Container[C], Container[A]]的超类型.您是类型判断员,不能像以前那样简单地接受T=A并结束它.

foo调用通过类型判断的唯一方法是类型判断器推断参数的类型是同构元组的列表,可以是tuple[Container[A], Container[A]],也可以是更具体的东西,如tuple[Container[B], Container[B]]tuple[Container[C], Container[C]]

不幸的是,这种方法依赖于您的类型判断器 Select specificheterogeneous-tuple类型,以使混合参数情况下的类型判断失败.这可能是个问题,因为如果您构造了一个足够多样化的参数列表,您的类型判断器可能不会推断出特定的异构元组类型,而是会发现tuple[Container[A], Container[A]]是最特定的超类型.

例如,表达式

[(c, b), (b, c)]

will be inferred 作为有类型的

list[tuple[Container[A], Container[A]]]

因此,如果将此参数would传递给foo,则会通过类型判断.


T = TypeVar("T", bound=A)时,代码为TL;DR:

reveal_type([(b, b), (b, b)])
reveal_type([(b, b), (c, c)])
reveal_type([(c, b), (c, c)])
reveal_type([(c, b), (b, c)])

将产生mypy个输出

Revealed type is "builtins.list[Tuple[Container[B], tmp.Container[tmp.B]]]"
Revealed type is "builtins.list[Tuple[builtins.object, builtins.object]]"
Revealed type is "builtins.list[Tuple[Container[C], builtins.object]]"
Revealed type is "builtins.list[Tuple[builtins.object, builtins.object]]"

但当为TypeVar("T", bound=A, covariant=True)时,它将产生mypy的输出

Revealed type is "builtins.list[Tuple[Container[B], Container[B]]]"
Revealed type is "builtins.list[Tuple[Container[A], Container[A]]]"
Revealed type is "builtins.list[Tuple[Container[C], Container[A]]]"
Revealed type is "builtins.list[Tuple[Container[A], Container[A]]]"

只要foo的参数被推断为具有单个被替换类型T的格式list[tuple[Container[T], Container[T]]]的类型,类型判断就会通过.使T协变使得True对于问题中的例子(第2行,很好!),但对于最混合的情况(第4行,不好!)也是正确的.

Python相关问答推荐

剧作家Python:expect(locator).to_be_visible()vs locator.wait_for()

如何通过多2多字段过滤查询集

使用polars .滤镜进行切片速度比pandas .loc慢

使用新的类型语法正确注释ParamSecdecorator (3.12)

海运图:调整行和列标签

如何从具有不同len的列表字典中创建摘要表?

加速Python循环

Python—从np.array中 Select 复杂的列子集

Pre—Commit MyPy无法禁用非错误消息

如何在UserSerializer中添加显式字段?

Python导入某些库时非法指令(核心转储)(beautifulsoup4."" yfinance)

numpy.unique如何消除重复列?

Numpyro AR(1)均值切换模型抽样不一致性

使用Python异步地持久跟踪用户输入

Django Table—如果项目是唯一的,则单行

使用SeleniumBase保存和加载Cookie时出现问题

如何使用Azure Function将xlsb转换为xlsx?

如何从比较函数生成ngroup?

仅使用预先计算的排序获取排序元素

PYTHON中的selenium不会打开 chromium URL