mypy 和基于输入参数返回子类列表的函数的方差

问题描述 投票:0回答:1

我正在尝试正确注释我的代码。这是重现我的问题的示例片段:

from pydantic import BaseModel
from typing import TypeVar


class Shape(BaseModel):
   name: str


class Circle(Shape):
   diameter: float


class Square(Shape):
   side: float


class Triangle(Shape):
   base: float
   height: float


T = TypeVar("T")


def generate_shapes(shapes: list[type[T]]) -> list[T]:
   generated_shapes = []
   for shape in shapes:
       if shape == Circle:
           generated_shapes.append(Circle(name=shape.__name__, diameter=10))
       elif shape == Square:
           generated_shapes.append(Square(name=shape.__name__, side=10))
       elif shape == Triangle:
           generated_shapes.append(Triangle(name=shape.__name__, base=10, height=10))
   return generated_shapes


if __name__ == "__main__":
   result = generate_shapes([Circle, Triangle])

实际上,代码看起来不错,但 mypy 抱怨。特别是我试图理解的错误是:

“generate_shapes”的参数 1 具有不兼容的类型 “列表[模型元类]”;预期“列表[类型[从不]]”

我的目标是能够从输入推断输出列表类型。看来使用这个注解vscode是能够看懂的:

我的基本理解是 mypy 抱怨是因为列表是可变的并且不能协变。所以我担心我想要实现的目标没有出路(vscode 推断 + mypy 快乐)?

python python-typing mypy pyright
1个回答
0
投票

你的函数并不是很通用。实际上,它需要一个

Shape
类列表并返回
Shape
列表。没有真正的理由“允许”使用更通用的列表作为参数。如果调用者以某种方式拥有像 [Circle, Triangle, 1, Square] 这样的列表,他们可以负责首先删除非
Shape
子类元素。
此外, 

generated_shapes

的初始推断类型是

list[Any]
,通过第一次尝试将任何内容追加到列表中,它会缩小为
list[Circle]
。如果您的函数不是通用的,您可以预先明确
generated_shapes
的类型。 (
mypy
并没有从函数声明的返回类型推断出
generated_shapes
的类型。)
def generate_shapes(shapes: list[type[Shape]]) -> list[Shape]:
   generated_shapes: list[Shape] = []
   for shape in shapes:
       if shape == Circle:
           generated_shapes.append(Circle(name=shape.__name__, diameter=10))
       elif shape == Square:
           generated_shapes.append(Square(name=shape.__name__, side=10))
       elif shape == Triangle:
           generated_shapes.append(Triangle(name=shape.__name__, base=10, height=10))
   return generated_shapes

	
© www.soinside.com 2019 - 2024. All rights reserved.