简体   繁体   English

在签名中注释特定的数据类子类

[英]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 is: error: "BaseProduct" has no attribute "a_specific_id" mypy 错误是: error: "BaseProduct" has no attribute "a_specific_id"

How to annotate AProductRequestSubmitter.__call__ properly?如何正确注释AProductRequestSubmitter.__call__ This class is specific for AProduct , there are other submitters for different product types.此 class 专用于AProduct ,还有针对不同产品类型的其他提交者。

Is it possible to use Generic types or Literal values somehow?是否可以以某种方式使用通用类型或文字值? Or maybe cast or assert is an only way to go?或者,强制转换或断言是 go 的唯一方法?

There are multiple ways to achieve what you are trying to, I will show the ones that pop out the top of my head right now.有多种方法可以实现您的目标,我将展示那些现在突然出现在我脑海中的方法。

Ensure correct type at runtime在运行时确保正确的类型

There may be multiple children having BaseProduct as their parent and there is no way for the type checker to know for sure, you will never pass a ProductSubmissionCommand instance that has product attribute of a type that is a child of BaseProduct that does not have a_specific_id attribute.可能有多个子级以BaseProduct作为其父级,并且类型检查器无法确定,您永远不会传递ProductSubmissionCommand实例,该实例具有的product属性类型是BaseProduct的子级,但没有a_specific_id属性. In order to make sure this can never happen, you could start your function call with a type assertion, such as:为了确保这永远不会发生,您可以使用类型断言开始您的 function 调用,例如:

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 (or any other static type checker) should now be quiet, because the program will never reach the part of the code, where a_specific_id is accessed unless the correct type is in use. Pycharm(或任何其他 static 类型检查器)现在应该是安静的,因为程序永远不会到达代码的一部分,除非使用了正确的类型,否则访问a_specific_id的部分。

Subclass ProductSubmissionCommand子类ProductSubmissionCommand

If you really plan on using AProductRequestSubmitter for instances, where ProductSubmissionCommand has product of type ProductA , you may subclass your ProductSubmissionCommand and typehint its product attribute as a ProductA instance.如果您确实打算将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

Again, all clever typecheckers should now understand, that cmd will always have product.a_specific_id attribute.同样,所有聪明的类型检查器现在都应该明白, cmd将始终具有product.a_specific_id属性。

Use Protocol使用Protocol

Using:使用:

class ProductASubmissionCommand(Protocol):
    product: ProductA

class AProductRequestSubmitter:
    def __call__(self, job_id: int, cmd: ProductASubmissionCommand):
        ...
        cmd.product.a_specific_id

You are basically saying "accept anything, that has an attribute named product of a type ProductA ".您基本上是在说“接受任何具有名为ProductA类型的product的属性”。 This is a little hacky and makes you create a protocol subclass only for the sake of typehinting this single method call, but, again, should work with all static type checkers.这有点 hacky,让您创建一个协议子类只是为了对这个单一的方法调用进行类型提示,但同样,它应该适用于所有 static 类型检查器。

You have 2 possible ways.你有两种可能的方法。

  1. Ignore static typint by using an explicit getattr call通过使用显式getattr调用忽略 static 类型

     class AProductRequestSubmitter: def __call__(self, job_id: int, cmd: ProductSubmissionCommand): ... getattr(cmd.product, 'a_specific_id')
  2. Use Generic to specialize ProductSubmissionCommand on a static typing point of view:使用Generic在 static 打字观点上专门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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM