matthewgdv
matthewgdv

Reputation: 749

Mypy: is there some way to implement a 'SelfType' TypeVar?

so this is a rather minor issue but I was wondering if anyone more familiar with the typing module might know if there's a way to do this.

I'd like to be able to define a type variable that is always equivalent to an instance of the class it is used inside (and if used by a subclass, equivalent to an instance of that subclass type).

What I'm currently doing to ensure mypy understands that the return value is equivalent to the class it is called on is annotating the 'self' and 'cls' arguments like so:

from typing import TypeVar, Type

T = TypeVar("T")


class Parent:
    @classmethod
    def from_classmethod(cls: Type[T]) -> T:
        return cls()

    def from_method(self: T) -> T:
        return type(self)()


class Child(Parent):
    pass


Child.from_classmethod()  # mypy: Revealed type is Child
Child().from_method()  # mypy: Revealed type is Child

And it does work, mypy will interpret these correctly as being class Child, not Parent.

However, I don't want to do it this way if I don't have to. I was wondering if there's some way to create a TypeVar that works like this:

SelfType = ...  # some voodoo magic goes here


class Parent:
    @classmethod
    def from_classmethod(cls) -> SelfType:
        return cls()

    def from_method(self) -> SelfType:
        return type(self)()


class Child(Parent):
    pass


# OtherParent is reusing SelfType without having to redefine it
class OtherParent:
    @classmethod
    def from_classmethod(cls) -> SelfType:
        return cls()

    def from_method(self) -> SelfType:
        return type(self)()


class OtherChild(OtherParent):
    pass


Child.from_classmethod()  # mypy: Revealed type is Child
Parent.from_classmethod()  # mypy: Revealed type is Parent
OtherChild.from_classmethod()  # mypy: Revealed type is OtherChild
OtherParent.from_classmethod()  # mypy: Revealed type is OtherParent

The main point being that I only ever have to define this TypeVar once and then be able to apply it to any class I want and have mypy contextually infer that the type is the same as the class it's called from (even if the method gets inherited).

Is this at all possible to do on our end, or would this require a feature request in the mypy project?

Upvotes: 4

Views: 4354

Answers (2)

Neil G
Neil G

Reputation: 33202

This is added by PEP 673 from Python 3.11: https://peps.python.org/pep-0673/

Upvotes: 1

Mihai Andrei
Mihai Andrei

Reputation: 1034

You have to define a typevar. See this discussion on the topic.

  T = TypeVar('T', bound='Copyable')
  class Copyable:
      def copy(self: T) -> T:
          # return a copy of self

Upvotes: 8

Related Questions