如何注释父类方法,使子方法返回自己的实例?



我有一个父类BaseBlob,它有几个子类LimitedProofId,ProofId,TxId。父类实现一个deserialize类方法,该方法应该返回一个自身的实例。

我也有一个Delegation类,需要LimitedProofId。我特别希望mypy在错误传递BaseBlob的另一个子实例(如ProofIdTxId)时出错。

from __future__ import annotations
from io import BytesIO

class BaseBlob:
def __init__(self, data: bytes):
self.data = data
@classmethod
def deserialize(cls, stream: BytesIO) -> BaseBlob:
return cls(stream.read(32))

class LimitedProofId(BaseBlob):
pass

class TxId(BaseBlob):
pass

class Delegation:
def __init__(self, ltd_id: LimitedProofId):
self.ltd_id = ltd_id
def deserialize(self, stream: BytesIO) -> Delegation:
ltd_id = LimitedProofId.deserialize(stream)
return Delegation(ltd_id)

mypy显示此代码的错误,因为它认为LimitedProofId.deserialize返回BaseBlob

error:参数1给" delegate "有不兼容的类型"BaseBlob";

我已经看到了使用T = TypeVar('T', bound='BaseBlob')来实现允许子类的类型注释的类似问题的答案,但是如果我这样做,我需要为BaseBlob.deserialize的返回类型和Delegation.__init__的第一个参数指定T,这违背了我对后者的类型安全的目的。

是否有一种方法来实现我想做的,而不必在所有子类上重新实现deserialize?

您希望表示deserialize返回它所绑定的类的一个实例。

Python>=3.9, <3.11

...
from typing import TypeVar
T = TypeVar("T", bound="BaseBlob")
class BaseBlob:
...
@classmethod
def deserialize(cls: type[T], stream: BytesIO) -> T:
return cls(stream.read(32))

这些变化使你上面发布的代码完全类型安全,mypy --strict也同意。

从子类(如LimitedProofId.deserialize)调用绑定方法,因此cls将是LimitedProofId,从类型化的角度来看,相应地将T绑定到type[T]中。


Python<3.9

和上面一样,但是从typing中导入Type,并用Type[T]替换type[T]注释。


Python>=3.11

@chepner说了什么。应该很快就可以使用mypy了。


澄清我不明白你的意思:(高亮显示)

我已经看到了使用T = TypeVar('T', bound='BaseBlob')来实现允许子类的类型注释的类似问题的答案,但如果我这样做,我需要为BaseBlob.deserialize的返回类型和Delegation.__init__的第一个参数指定T,这违背了我对后者类型安全的目的。

__init__与此有什么关系?

如果我没有理解你的意思,请详细说明,我将尽力调整答案。


附录

由于您似乎不确定这些注释如何影响类型检查器推断相关类型的方式,这里是一个完整的演示:

from __future__ import annotations
from io import BytesIO
from typing import TypeVar
T = TypeVar("T", bound="BaseBlob")
class BaseBlob:
def __init__(self, data: bytes):
self.data = data
@classmethod
def deserialize(cls: type[T], stream: BytesIO) -> T:
return cls(stream.read(32))
class LimitedProofId(BaseBlob):
pass
class TxId(BaseBlob):
pass
class Delegation:
def __init__(self, ltd_id: LimitedProofId):
self.ltd_id = ltd_id
@classmethod
def deserialize(cls, stream: BytesIO) -> Delegation:
ltd_id = LimitedProofId.deserialize(stream)
return cls(ltd_id)
if __name__ == "__main__":
ltd = LimitedProofId(b"xyz")
d = Delegation.deserialize(BytesIO(b"abc"))
reveal_type(d.ltd_id)
reveal_type(LimitedProofId.deserialize(BytesIO()))
reveal_type(TxId.deserialize(BytesIO()))

mypy --strict输出无错误:

注:显示类型为" limitedprofid "注:显示类型为"limitedprofid"注:显示类型为"TxId"成功:1个源文件
未发现问题

Python 3.11为此引入了Self类型提示。(PEP 673更详细地描述了Self旨在简化的代码,如果您还没有升级到3.11。)

from typing import Self

class BaseBlob:
def __init__(self, data: bytes):
self.data = data
@classmethod
def deserialize(cls, stream: BytesIO) -> Self:
return cls(stream.read(32))

最新更新