我有一个父类BaseBlob
,它有几个子类LimitedProofId
,ProofId
,TxId
。父类实现一个deserialize
类方法,该方法应该返回一个自身的实例。
我也有一个Delegation
类,需要LimitedProofId
。我特别希望mypy在错误传递BaseBlob
的另一个子实例(如ProofId
或TxId
)时出错。
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))