Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.9k views
in Technique[技术] by (71.8m points)

Can a method in a python class be annotated with a type that is defined by a subclass?

I have a superclass that has a method which is shared by its subclasses. However, this method should return an object with a type that is defined on the subclass. I'd like the return type for the method to be statically annotated (not a dynamic type) so code using the subclasses can benefit from mypy type checking on the return values. But I don't want to have to redefine the common method on the subclass just to provide its type annotation. Is this possible with python type annotations and mypy?

Something like this:

from typing import Type

class AbstractModel:
    pass

class OrderModel(AbstractModel):
    def do_order_stuff():
        pass

class AbstractRepository:
    model: Type[AbstractModel]

    def get(self) -> model:
        return self.model()

class OrderRepository(AbstractRepository):
    model = OrderModel


repo = OrderRepository()
order = repo.get()

# Type checkers (like mypy) should recognize that this is valid
order.do_order_stuff()

# Type checkers should complain about this; because `OrderModel`
# does not define `foo`
order.foo()

The tricky move here is that get() is defined on the superclass AbstractRepository, which doesn't yet know the type of model. (And the -> model annotation fails, since the value of model hasn't been specified yet).

The value of model is specified by the subclass, but the subclass doesn't (re)define get() in order to provide the annotation. It seems like this should be statically analyzable; though it's a little tricky, since it would require the static analyzer to trace the model reference from the superclass to the subclass.

Any way to accomplish both a shared superclass implementation and a precise subclass return type?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Define AbstractRepository as a generic class.

from typing import TypeVar, Generic, Type, ClassVar


T = TypeVar('T')

class AbstractRespotitory(Generic[T]):
    model: ClassVar[Type[T]]

    @classmethod
    def get(cls) -> T:
        return cls.model()

(get only makes use of a class attribute, so can--and arguably should--be a class method.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...