代码之家  ›  专栏  ›  技术社区  ›  kurtgn

mypy:创建接受子类实例列表的类型

  •  2
  • kurtgn  · 技术社区  · 6 年前

    假设我有一个 Child Parent 类和一个函数,该函数接受 父母亲

    from typing import List
    
    
    class Parent:
        pass
    
    
    class Child(Parent):
        pass
    
    
    def func(objects: List[Parent]) -> None:
        print(objects)
    
    
    children = [Child()]
    func(children)
    

    跑步 mypy

     error: Argument 1 to "func" has incompatible type "List[Child]"; expected "List[Parent]"
    

    Sequence 类型:

    def func(objects: Sequence[Parent]) -> None:
        print(objects)
    

    但在其他类似的情况下,这并没有帮助。我需要一个 List 序列 .

    1 回复  |  直到 4 年前
        1
  •  7
  •   Michael0x2a    6 年前

    在这里传递列表从根本上讲是不安全的。例如,如果你这样做呢?

    def func(objects: List[Parent]) -> None:
        print(objects)
        objects.append(Parent())
    
    children: List[Child] = [Child(), Child(), Child()]
    func(children)
    # Uh-oh! 'children' contains a Parent()!
    

    如果允许进行类型检查,那么您的代码将最终包含一个bug。

    使用打字行话, List 故意设计成 不变的 Child 是的一个子类 Parent ,但事实并非如此 List[Child] 是的子类型 List[Parent] 反之亦然。你可以找到更多关于不变性的信息 here here .

    最常见的方法是使用 Sequence 相反,它是一个只读接口/协议/什么的。因为序列是只读的,所以它是安全的 协变的 Sequence[Child] 被认为是的有效子类型 Sequence[Parent] .

    type variables 相反例如,不要说“this function takes in a list of Parent”,而是说“this function takes in a list of any class which is Parent,or a subclass of Parent”:

    TParent = TypeVar('TParent', bound=Parent)
    
    def func(objects: List[TParent]) -> List[TParent]:
        print(objects)
    
        # Would not typecheck: we can't assume 'objects' will be a List[Parent]
        objects.append(Parent())  
    
        return objects
    

    根据你到底在做什么,你可以创建一个 custom Protocol 逆变 --也就是说, WriteOnlyThing[Parent] 可能是 WriteOnlyThing[Child] func 写东西[孩子] 在两种情况下都能安全通过 写东西[孩子] .

    如果这两种方法都不适用于您的情况,那么您唯一的办法就是使用这两种方法中的任何一种 # type: ignore List[Any] (也不推荐),或者找出如何重新构造代码以使其具有类型安全性。