首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在python中使用TypeVar进行多个通用协议的输入和输出?

如何在python中使用TypeVar进行多个通用协议的输入和输出?
EN

Stack Overflow用户
提问于 2021-05-05 15:14:30
回答 1查看 2.1K关注 0票数 3

我希望使用多个通用协议并确保它们是兼容的:

代码语言:javascript
复制
from typing import TypeVar, Protocol, Generic
from dataclasses import dataclass

# checking fails as below and with contravariant=True or covariant=True:
A = TypeVar("A") 

class C(Protocol[A]):
    def f(self, a: A) -> None: pass

class D(Protocol[A]):
    def g(self) -> A: pass

# Just demonstrates my use case; doesn't have errors:
@dataclass
class CompatibleThings(Generic[A]):
    c: C[A]
    d: D[A]

Mypy给出了以下错误:

代码语言:javascript
复制
Invariant type variable 'A' used in protocol where contravariant one is expected
Invariant type variable 'A' used in protocol where covariant one is expected

我知道这可以通过创建CD泛型ABC类来完成,但我想使用协议。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-05 21:47:20

简短的解释是,您的方法破坏了子类型的传递性;有关更多信息,请参见PEP 544的这一节。它非常清楚地解释了为什么您的D协议(以及隐含的C协议)会遇到这个问题,以及为什么每个协议都需要不同类型的差异才能解决这个问题。您还可以查看维基百科获取类型差异的信息。

以下是解决办法:使用协变和反变体协议,但使您的一般数据集不变。这里最大的障碍是继承,为了使用协议,您必须处理它,但这与您的目标有一定的关系。我将在这里切换命名,突出显示所起作用的继承,这就是这一切的意义所在:

代码语言:javascript
复制
A = TypeVar("A") # Invariant type
A_cov = TypeVar("A_cov", covariant=True) # Covariant type
A_contra = TypeVar("A_contra", contravariant=True) # Contravariant type

# Give Intake its contravariance
class Intake(Protocol[A_contra]):
    def f(self, a: A_contra) -> None: pass

# Give Output its covariance
class Output(Protocol[A_cov]):
    def g(self) -> A_cov: pass

# Just tell IntakeOutput that the type needs to be the same
# Since a is invariant, it doesn't care that
# Intake and Output require contra / covariance
@dataclass
class IntakeOutput(Generic[A]):
    intake: Intake[A]
    output: Output[A]

您可以看到这适用于以下测试:

代码语言:javascript
复制
class Animal:
    ...
    
class Cat(Animal):
    ...
    
class Dog(Animal):
    ...
    
class IntakeCat:
    def f(self, a: Cat) -> None: pass

class IntakeDog:
    def f(self, a: Dog) -> None: pass

class OutputCat:
    def g(self) -> Cat: pass

class OutputDog:
    def g(self) -> Dog: pass

compat_cat: IntakeOutput[Cat] = IntakeOutput(IntakeCat(), OutputCat())
compat_dog: IntakeOutput[Dog] = IntakeOutput(IntakeDog(), OutputDog())

# This is gonna error in mypy
compat_fail: IntakeOutput[Dog] = IntakeOutput(IntakeDog(), OutputCat())

这将产生以下错误:

代码语言:javascript
复制
main.py:48: error: Argument 2 to "IntakeOutput" has incompatible type "OutputCat"; expected "Output[Dog]"
main.py:48: note: Following member(s) of "OutputCat" have conflicts:
main.py:48: note:     Expected:
main.py:48: note:         def g(self) -> Dog
main.py:48: note:     Got:
main.py:48: note:         def g(self) -> Cat

那有什么可抓的?你要放弃什么?即IntakeOutput中的继承。以下是你所不能做的:

代码语言:javascript
复制
class IntakeAnimal:
    def f(self, a: Animal) -> None: pass

class OutputAnimal:
    def g(self) -> Animal: pass

# Ok, as expected
ok1: IntakeOutput[Animal] = IntakeOutput(IntakeAnimal(), OutputAnimal())

# Ok, because Output is covariant
ok2: IntakeOutput[Animal] = IntakeOutput(IntakeAnimal(), OutputDog())

# Both fail, because Intake is contravariant
fails1: IntakeOutput[Animal] = IntakeOutput(IntakeDog(), OutputDog())
fails2: IntakeOutput[Animal] = IntakeOutput(IntakeDog(), OutputAnimal())

# Ok, because Intake is contravariant
ok3: IntakeOutput[Dog] = IntakeOutput(IntakeAnimal(), OutputDog())

# This fails, because Output is covariant
fails3: IntakeOutput[Dog] = IntakeOutput(IntakeAnimal(), OutputAnimal())
fails4: IntakeOutput[Dog] = IntakeOutput(IntakeDog(), OutputAnimal())

所以。这就是了。你可以玩这个更多的这里

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67404067

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档