Tristan Duquesne
Tristan Duquesne

Reputation: 672

Python: type-hinting a classmethod that returns an instance of the class, for a class that is inherited

Consider the following:

from __future__ import annotations

class A:

    def __init__(self):
        print("A")
        self.hello = "hello"

    # how do I type this so that the return type is A for A.bobo()
    # and B for B.bobo()?
    @classmethod
    def bobo(cls) -> UnknownType:
        return cls()


class B(A):

    def __init__(self):
        print("B")
        super().__init__()
        self.world = "world"


instance_of_B = B.bobo()  # prints "B", then "A", and returns an instance of B

I want to type-hint the bobo class method, so that mypy can know that in the case of B's bobo method, it's not just an instance of A that's returned, but actually an instance of B. I'm really unclear on how to do that, or if it's even possible. I thought that something like Type[cls] might do the trick, but I'm not sure if that even makes syntactic sense to mypy.

Upvotes: 14

Views: 11322

Answers (2)

decorator-factory
decorator-factory

Reputation: 3083

For an example of how to apply Brian's answer:

from typing import TypeVar


AnyA = TypeVar("AnyA", bound="A")


class A:

    def __init__(self):
        print("A")
        self.hello = "hello"

    @classmethod
    def bobo(cls: type[AnyA]) -> AnyA:
        return cls()

class B(A):

    def __init__(self):
        print("B")
        super().__init__()
        self.world = "world"

reveal_type(B.bobo())  # B
  • with Self:
from typing import Self

class A:

    def __init__(self):
        print("A")
        self.hello = "hello"

    @classmethod
    def bobo(cls) -> Self:
        return cls()

class B(A):

    def __init__(self):
        print("B")
        super().__init__()
        self.world = "world"

reveal_type(B.bobo())  # B

If your version of Python doesn't have Self yet, you can use the typing-extensions package, which serves as backport for some typing features:

- from typing import Self
+ from typing_extensions import Self

Upvotes: 27

Brian
Brian

Reputation: 438

You will have to use a TypeVar, thankfully, in Python 3.11 the typing.Self type is coming out. This PEP describes it in detail. It also specifies how to use the TypeVar until then.

Upvotes: 6

Related Questions