在签名中注释特定的数据类子类
Annotating specific dataclass subclass in a signature
from dataclasses import dataclass
@dataclass
class BaseProduct:
...
@dataclass
class ProductA(BaseProduct):
a_specific_id: int
@dataclass
class ProductSubmissionCommand:
product_id: str
product: BaseProduct
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductSubmissionCommand):
...
cmd.product.a_specific_id
mypy 错误是:
error: "BaseProduct" has no attribute "a_specific_id"
如何正确注释AProductRequestSubmitter.__call__
?此 class 特定于 AProduct
,还有针对不同产品类型的其他提交者。
是否可以以某种方式使用通用类型或文字值?或者 cast 或 assert 是唯一的方法吗?
有多种方法可以实现您的目标,我将展示我现在想到的那些。
确保运行时类型正确
可能有多个 children 将 BaseProduct
作为他们的 parent 并且类型检查器无法确定地知道,你永远不会通过 ProductSubmissionCommand
具有 product
类型属性的实例,该类型是 BaseProduct
的 child,没有 a_specific_id
属性。为了确保这永远不会发生,您可以使用类型断言开始函数调用,例如:
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductSubmissionCommand):
if not isinstance(cmd.product, ProductA):
raise TypeError(f"Only 'ProductA' instances are allowed, not '{cmd.product.__class__.__name__}'")
...
cmd.product.a_specific_id
Pycharm(或任何其他静态类型检查器)现在应该是安静的,因为程序永远不会到达代码的一部分,除非使用正确的类型,否则将访问 a_specific_id
.
子类ProductSubmissionCommand
如果您真的打算使用 AProductRequestSubmitter
作为实例,其中 ProductSubmissionCommand
具有类型 ProductA
的 product
,您可以子类化您的 ProductSubmissionCommand
和类型提示其 product
属性作为 ProductA
实例。
@dataclass
class ProductASubmissionCommand(ProductSubmissionCommand):
product: ProductA
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductASubmissionCommand):
...
cmd.product.a_specific_id
同样,所有聪明的类型检查员现在都应该明白,cmd
将始终具有 product.a_specific_id
属性。
使用Protocol
使用:
class ProductASubmissionCommand(Protocol):
product: ProductA
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductASubmissionCommand):
...
cmd.product.a_specific_id
您基本上是在说“接受任何具有 ProductA
类型的名为 product
的属性”。这有点 hacky,让你创建一个协议子类只是为了类型提示这个单一的方法调用,但是,同样,应该与所有静态类型检查器一起工作。
您有 2 种可能的方法。
使用显式 getattr
调用忽略静态打字
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductSubmissionCommand):
...
getattr(cmd.product, 'a_specific_id')
使用Generic
专业化 ProductSubmissionCommand
从静态类型的角度来看:
T = TypeVar("T", bound=BaseProduct)
@dataclass
class ProductSubmissionCommand(Generic[T]):
product_id: str
product: T
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductSubmissionCommand[ProductA]):
pass
from dataclasses import dataclass
@dataclass
class BaseProduct:
...
@dataclass
class ProductA(BaseProduct):
a_specific_id: int
@dataclass
class ProductSubmissionCommand:
product_id: str
product: BaseProduct
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductSubmissionCommand):
...
cmd.product.a_specific_id
mypy 错误是:
error: "BaseProduct" has no attribute "a_specific_id"
如何正确注释AProductRequestSubmitter.__call__
?此 class 特定于 AProduct
,还有针对不同产品类型的其他提交者。
是否可以以某种方式使用通用类型或文字值?或者 cast 或 assert 是唯一的方法吗?
有多种方法可以实现您的目标,我将展示我现在想到的那些。
确保运行时类型正确
可能有多个 children 将 BaseProduct
作为他们的 parent 并且类型检查器无法确定地知道,你永远不会通过 ProductSubmissionCommand
具有 product
类型属性的实例,该类型是 BaseProduct
的 child,没有 a_specific_id
属性。为了确保这永远不会发生,您可以使用类型断言开始函数调用,例如:
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductSubmissionCommand):
if not isinstance(cmd.product, ProductA):
raise TypeError(f"Only 'ProductA' instances are allowed, not '{cmd.product.__class__.__name__}'")
...
cmd.product.a_specific_id
Pycharm(或任何其他静态类型检查器)现在应该是安静的,因为程序永远不会到达代码的一部分,除非使用正确的类型,否则将访问 a_specific_id
.
子类ProductSubmissionCommand
如果您真的打算使用 AProductRequestSubmitter
作为实例,其中 ProductSubmissionCommand
具有类型 ProductA
的 product
,您可以子类化您的 ProductSubmissionCommand
和类型提示其 product
属性作为 ProductA
实例。
@dataclass
class ProductASubmissionCommand(ProductSubmissionCommand):
product: ProductA
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductASubmissionCommand):
...
cmd.product.a_specific_id
同样,所有聪明的类型检查员现在都应该明白,cmd
将始终具有 product.a_specific_id
属性。
使用Protocol
使用:
class ProductASubmissionCommand(Protocol):
product: ProductA
class AProductRequestSubmitter:
def __call__(self, job_id: int, cmd: ProductASubmissionCommand):
...
cmd.product.a_specific_id
您基本上是在说“接受任何具有 ProductA
类型的名为 product
的属性”。这有点 hacky,让你创建一个协议子类只是为了类型提示这个单一的方法调用,但是,同样,应该与所有静态类型检查器一起工作。
您有 2 种可能的方法。
使用显式
getattr
调用忽略静态打字class AProductRequestSubmitter: def __call__(self, job_id: int, cmd: ProductSubmissionCommand): ... getattr(cmd.product, 'a_specific_id')
使用
Generic
专业化ProductSubmissionCommand
从静态类型的角度来看:T = TypeVar("T", bound=BaseProduct) @dataclass class ProductSubmissionCommand(Generic[T]): product_id: str product: T class AProductRequestSubmitter: def __call__(self, job_id: int, cmd: ProductSubmissionCommand[ProductA]): pass