Home > Blockchain >  mypy: How to declare the return type of a method returning self in a generic class?
mypy: How to declare the return type of a method returning self in a generic class?

Time:05-22

This answer does not seem to work for generics. Mypy complains about "error: Missing type parameters for generic type A" when checking the following code. I have tried using 'A[T]' for the TypeVar but then mypy says "error: Type variable T is unbound." I have also tried using AnyA[T] as return type of get but that produces two error messages, the already known "error: Missing type parameters for generic type A" and the new error message "Type variable AnyA used with arguments".

How do I specify the return type of get correctly?

import typing

T = typing.TypeVar('T')
AnyA = typing.TypeVar('AnyA', bound='A')

class A(typing.Generic[T]):

    def __init__(self, val: T) -> None:
        self.val = val

    def get(self: AnyA) -> AnyA:
        return self

class B(A[T]):
    def is_int(self) -> bool:
        return isinstance(self.val, int)


if __name__ == '__main__':
    b = B(42)
    print(b.get().is_int())

CodePudding user response:

I know of three ways of typing here:

Declaring an inner self-type

This approach is described in mypy docs, see Precise typing of alternative constructors.

class A(typing.Generic[T]):
    _Self = typing.TypeVar('_Self', bound='A[T]')

    def __init__(self, val: T) -> None:
        self.val = val

    def get(self: _Self) -> _Self:
        return self

Note however, that this is mypy-specific stuff and may not work with other checkers. E.g. pyre doesn't support inner self-types yet.

Using _typeshed.Self

This saves the boilerplate of declaring custom types, but requires a somewhat obscure import from typeshed which will fail at runtime. It thus must be wrapped by typing.TYPE_CHECKING:

from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    from _typeshed import Self
else:
    Self = Any

class A(typing.Generic[T]):
    def __init__(self, val: T) -> None:
        self.val = val

    def get(self: Self) -> Self:
        return self

_typeshed.Self was created to be used in custom stubs in the first place, but is suitable for inline typing as well.

Python 3.11 and upwards: typing.Self

A recently introduced PEP 673 adds Self to stdlib, so starting from Python 3.11 one will be able to use that:

from typing import Self

class A(typing.Generic[T]):
    def __init__(self, val: T) -> None:
        self.val = val

    def get(self: Self) -> Self:
        return self

This is not supported by mypy yet as of now though, but e.g. by pyright from version 1.1.184.

  • Related