【问题标题】:How do you get Mypy to enforce invariance, contravariance, and covariance for mutable containers你如何让 Mypy 为可变容器强制执行不变性、逆变性和协变
【发布时间】:2021-07-26 11:51:06
【问题描述】:

我正在尝试使用 typehints 和 Mypy(也是 PyCharm)来强制容器的变化,请参阅,butchered,代码如下:

from typing import TypeVar, Generic

class T: ...
class M(T): ...
class B(M): ...

InMT = TypeVar('InMT', bound=M)
ContraMT = TypeVar('ContraMT', bound=M, contravariant=True)
CoMT = TypeVar('CoMT', bound=M, covariant=True)

class In(Generic[InMT]):
    x: InMT
class Contra(Generic[ContraMT]):
    x: ContraMT
class Co(Generic[CoMT]):
    x: CoMT

t = T()
m = M()
b = B()

m_in: In[M] = In()
m_contra: Contra[M] = Contra()
m_co: Co[M] = Co()

m_in.x = t  # mypy: Incompatible types in assignment (expression has type "T", variable has type "M").
m_in.x = m
m_in.x = b

m_contra.x = t # mypy: Incompatible types in assignment (expression has type "T", variable has type "M").
m_contra.x = m
m_contra.x = b

m_co.x = t # mypy: Incompatible types in assignment (expression has type "T", variable has type "M").
m_co.x = m
m_co.x = b

Mypy 发现了一些问题,见上面代码中的 cmets,PyCharm 没有发现!但是我认为 Mypy 遗漏了许多问题,错误地报告了问题,并给出了误导性的错误消息:

  1. m_in.x = t 的错误消息是错误的,因为变量的类型是 InMT 而不是 M

  2. m_in.x = b 应该是一个错误,因为 B 不是 InMT(只有 M 是)。

  3. m_contra.x = t 应该是错误,因为 TContraMT

  4. m_contra.x = b 应该是一个错误,因为 B 不是 ContraMT(只有 MT 是)。

  5. 如上所述,变量的类型为CoMT 而不是M

我做错了什么;还是我误解了 Mypy 的用途?

【问题讨论】:

    标签: python pycharm type-hinting mypy variance


    【解决方案1】:

    我正在使用 PyCharm 2021.3.2(撰写本文时的最新版本),但我没有看到不变性、逆变性和协方差类型检查正常工作。它似乎(错误地)一直假设协方差。尽管 Pycharm claims support.

    PEP 484 code 下面删除了协方差标志不会标记错误,我认为它应该是:

    默认情况下,泛型类型在所有类型变量中都被认为是不变的,这意味着使用 List[Employee] 等类型注释的变量的值必须与类型注释完全匹配——没有类型参数的子类或超类(在本例中为 Employee ) 是允许的。

    from typing import TypeVar, Generic, Iterable, Iterator
    
    T = TypeVar('T', covariant=True)
    
    class ImmutableList(Generic[T]):
        def __init__(self, items: Iterable[T]) -> None: ...
        def __iter__(self) -> Iterator[T]: ...
        ...
    
    class Employee: ...
    
    class Manager(Employee): ...
    
    def dump_employees(emps: ImmutableList[Employee]) -> None:
        for emp in emps:
            ...
    
    mgrs = ImmutableList([Manager()])  # type: ImmutableList[Manager]
    dump_employees(mgrs)  # Should be caught by type-checker
    
    

    它也没有被抓住: mgrs: ImmutableList[Manager] = ImmutableList([Manager()])

    也不会被内置列表捕获:

    def dump_more_employees(employees: list[Employee]) -> None:
        for employee in employees:
            ...
    
    
    managers: list[Manager] = list([Manager()])
    dump_more_employees(managers)  # Should be caught by type-checker
    

    什么时候应该:

    考虑一个 Employee 类和一个子类 Manager。现在假设我们有一个带有用 List[Employee] 注释的参数的函数。是否应该允许我们使用 List[Manager] 类型的变量作为其参数来调用此函数?许多人会回答“是的,当然”,甚至不考虑后果。但是除非我们对函数有更多了解,否则类型检查器应该拒绝这样的调用:函数可能会将一个 Employee 实例附加到列表中,这会违反调用者中变量的类型。

    我无法与 MyPy 交谈,但我希望这很有用。

    【讨论】:

      猜你喜欢
      • 2014-03-06
      • 1970-01-01
      • 2019-06-21
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      • 2016-11-03
      • 2015-02-09
      • 2012-04-06
      相关资源
      最近更新 更多