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"
How to annotate AProductRequestSubmitter.__call__
properly? This class is specific for AProduct
, there are other submitters for different product types.
Is it possible to use Generic types or Literal values somehow? Or maybe cast or assert is an only way to 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.
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. In order to make sure this can never happen, you could start your function call with a type assertion, such as:
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.
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.
@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.
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
". 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.
You have 2 possible ways.
Ignore static typint by using an explicit getattr
call
class AProductRequestSubmitter: def __call__(self, job_id: int, cmd: ProductSubmissionCommand): ... getattr(cmd.product, 'a_specific_id')
Use Generic
to specialize ProductSubmissionCommand
on a static typing point of view:
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.