让子类中的方法接受父类签名中参数的子类



我认为如果我提供一个PoC片段而不是用文字解释它是最简单的:

from abc import ABCMeta, abstractmethod
from typing import Any, Dict

class A:
a: int

class B(A):
b: str

class Getter:
@abstractmethod
def get(self, o: A) -> Dict[str, Any]:
pass

class BGetter(Getter):
def get(self, o: B) -> Dict[str, Any]:
return {
'a': o.a,
'b': o.b,
}

当在这段代码上运行mypy时,我得到以下错误:

Argument 1 of "get" is incompatible with supertype "Getter"; supertype defines the argument type as "A"

我理解它希望BGetter中的get方法具有与父方法相同的签名,但为什么如果参数是a的子类?有没有一种方法可以告诉mymyy,这是可以的,它应该接受子类中的方法参数的子类?

我偶然发现了一个解决方案,尽管对某些人来说可能有些粗糙。

from abc import abstractmethod
from typing import Any, Generic, Protocol, TypeVar

class A:
a: int = 2

class B(A):
b: str = 'string'

T = TypeVar('T', contravariant=True)

class Getter(Generic[T], Protocol):
@ abstractmethod
def get(self, o: T) -> dict[str, Any]:
pass

class AGetter(Getter[A]):
def get(self, o: A) -> dict[str, Any]:
return {
'a': o.a,
}

class BGetter(Getter[B]):
def get(self, o: B) -> dict[str, Any]:
return {
'a': o.a,
'b': o.b,
}

def fa(getter: Getter[A], o: A) -> dict[str, Any]:
return getter.get(o)

def fb(getter: Getter[B], o: B) -> dict[str, Any]:
return getter.get(o)

print(fa(AGetter(), A()))
print(fa(AGetter(), B()))
print(fb(BGetter(), B()))
print(fb(BGetter(), A()))  # Expected mypy error

除了最后一行以外的所有内容都将通过我的。最后一行预计会出错,因为BGetter期望至少有一个b的实例。唯一不合适的地方是:

def f(getter: Getter, o: A) -> dict[str, Any]:
return getter.get(o)

print(f(BGetter(), A()))

Mypy在这种情况下不会抱怨,因为两个参数都是正确的类型,但这会导致运行时失败,因为BGetter期望在提供的类中找到b属性。

最新更新