为了账号安全,请及时绑定邮箱和手机立即绑定

为什么 mypy 会忽略包含与 TypeVar 不兼容的类型的泛型类型变量?

为什么 mypy 会忽略包含与 TypeVar 不兼容的类型的泛型类型变量?

陪伴而非守候 2021-12-17 16:51:35
下面我定义了类型变量、泛型类型别名和一个点积函数。mypy不会引发错误。为什么不?我希望它会引发错误,v3因为它是一个字符串向量,并且我已经指定它T必须是int, float, 或complex。from typing import Any, Iterable, Tuple, TypeVarT = TypeVar('T', int, float, complex)Vector = Iterable[T]def dot_product(a: Vector[T], b: Vector[T]) -> T:    return sum(x * y for x, y in zip(a, b))v1: Vector[int] = []    # same as Iterable[int], OKv2: Vector[float] = []  # same as Iterable[float], OKv3: Vector[str] = []    # no error - why not?
查看完整描述

1 回答

?
繁华开满天机

TA贡献1816条经验 获得超4个赞

我认为这里的问题在于,当您构建类型别名时,您实际上并不是在构建新类型——您只是为现有类型提供一个昵称或替代拼写。


如果您所做的只是为类型提供替代拼写,则意味着在这样做时应该不可能添加任何额外的行为。这正是这里发生的事情:您正在尝试向 Iterable 添加其他信息(您的三种类型约束),而 mypy 忽略了它们。在关于泛型类型别名的mypy 文档的底部有一个注释基本上说明了这一点。


事实上,mypy 只是默默地使用你的 TypeVar 而没有警告它的附加约束被忽略的事实感觉就像一个错误。具体来说,它感觉像是一个可用性错误:Mypy 应该在这里发出警告,并禁止在类型别名中使用除不受限制的类型变量之外的任何其他内容。


那么你能做些什么来输入你的代码呢?


嗯,一个干净的解决方案是不打扰创建Vector类型别名——或者创建它,但不用担心限制它可以参数化的内容。


这意味着用户可以创建一个Vector[str](又名 an Iterable[str]),但这真的没什么大不了的:当他们尝试将它实际传递给任何函数时,他们会得到一个类型错误,比如你的dot_product函数,它确实使用了类型别名。


第二种解决方案是创建自定义vector子类。如果这样做,您将创建一个新类型,因此实际上可以添加新约束——但您将不再能够将列表等直接传递到您的dot_product类中:您需要将它们包装在您的自定义中向量类。


这可能有点笨拙,但无论如何您最终可能会转向这个解决方案:它使您有机会向新的 Vector 类添加自定义方法,这可能有助于提高代码的整体可读性,具体取决于您的具体内容正在做。


第三个也是最后一个解决方案是定义一个自定义的“矢量”协议。这将使我们不必将我们的列表包装在某个自定义类中——并且我们正在创建一个新类型,以便我们可以添加我们想要的任何约束。例如:


from typing import Iterable, TypeVar, Iterator, List

from typing_extensions import Protocol


T = TypeVar('T', int, float, complex)


# Note: "class Vector(Protocol[T])" here means the same thing as 

# "class Vector(Protocol, Generic[T])".

class Vector(Protocol[T]):

    # Any object that implements these three methods with a compatible signature

    # is considered to be compatible with "Vector".


    def __iter__(self) -> Iterator[T]: ...


    def __getitem__(self, idx: int) -> T: ...


    def __setitem__(self, idx: int, val: T) -> None: ...


def dot_product(a: Vector[T], b: Vector[T]) -> T:

    return sum(x * y for x, y in zip(a, b))


v1: Vector[int] = []    # OK: List[int] is compatible with Vector[int]

v2: Vector[float] = []  # OK: List[float] is compatible with Vector[int]

v3: Vector[str] = []    # Error: Value of type variable "T" of "Vector" cannot be "str"


dot_product(v3, v3)  # Error: Value of type variable "T" of "dot_product" cannot be "str"


nums: List[int] = [1, 2, 3]

dot_product(nums, nums)  # OK: List[int] is compatible with Vector[int]

这种方法的主要缺点是您无法真正向您的协议添加任何具有实际逻辑的方法,您可以在任何可能被视为“向量”的东西之间重用这些方法。(好吧,您可以,但不会以任何方式在您的示例中有用)。


查看完整回答
反对 回复 2021-12-17
  • 1 回答
  • 0 关注
  • 231 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号