Typing: How can I annotate a classmethod as returning an object of type cls?

Created on 27 Jul 2016  路  6Comments  路  Source: python/typing

If, for example, I have classmethod in the base class:

class BaseClass:
    @classmethod
    def from_list(cls, **kwargs):
        return cls(**kwargs)

and I'm going to create SubClass with this it's own __init__().

How can I annotate classmethod to return not the BaseClass, but an object of the SubClass, without reimplementing from_list method?

question

Most helpful comment

Guido gave the solution to this in his first answer:

_T = TypeVar("_T")

class Foo:
    @classmethod
    def create(cls: Type[_T]) -> _T:
        return cls()

All 6 comments

I'm not sure how to do it with a class method, but you can easily do this with a helper function that serves as a factory function:

from typing import TypeVar, Type

class B:
  pass

T = TypeVar('T', bound=B)

def foo(cls: Type[T]) -> T:
  return cls()

class C(B):
  pass

reveal_type(foo(C))

class D(C):
  pass

reveal_type(foo(D))

The first reveal call shows a return value of type C, the second shows type D.

You _ought_ to be able to do this with a class method, by annotating the cls argument with Type[T], but I couldn't get that to work; that seems a bug in mypy (or at least a missing feature).

Thank for the example @gvanrossum. Also, thanks for the referenced issue, it contains solution for almost exactly the same problem. I'm closing the issue.

Coming in from Google so if anyone is looking, this will be possible in Python 3.7:

https://docs.python.org/dev/whatsnew/3.7.html#pep-563-postponed-evaluation-of-annotations

I don't think that PEP 563 has much bearing on this issue -- whatever you can do with PEP 563 you can do without it by adding quotes around the forward reference.

Coming in from Google so if anyone is looking, this will be possible in Python 3.7:

docs.python.org/dev/whatsnew/3.7.html#pep-563-postponed-evaluation-of-annotations

What Guido said: the issue isn't that you can't annotate the type when you _know_ the type. The issue is that you want to use cls as an annotation in arguments and/or for the return type of a @classmethod. There still seems no easy way of doing this apparently :/

Guido gave the solution to this in his first answer:

_T = TypeVar("_T")

class Foo:
    @classmethod
    def create(cls: Type[_T]) -> _T:
        return cls()
Was this page helpful?
0 / 5 - 0 ratings

Related issues

feluxe picture feluxe  路  8Comments

gvanrossum picture gvanrossum  路  8Comments

FuegoFro picture FuegoFro  路  8Comments

Phlogistique picture Phlogistique  路  4Comments

sid-kap picture sid-kap  路  7Comments