-
After upgrading to Pyright 1.1.354, code which uses a classmethod as a constructor for a generic class began triggering a type-checking error. For example: class Foo[T]:
@classmethod
def from_value(cls, value: T) -> Self:
return cls(value)
def __init__(self, value: T) -> None:
self._value = value
# Error: Type of a is Foo[Unknown]
a = Foo.from_value(1) I read #7445 & #5491, and looked through the typing reference for generics, but I am still unsure whether the code in question should be changed or not. I understand that the type of So, should construction using classmethods now always specify the generic class type, like this? b = Foo[type(1)].from_value(1) |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
A class method is bound to a specific class type. If that class is generic, the type arguments for the type are dictated by the (specialized) class type to which it is bound. Unlike class methods, constructors are special-cased in the type system (and in type checkers). They determine the type arguments for a generic class at construction time even if the class being constructed is not explicitly specialized. Class methods are not like this. If you're using a class method as an alternative constructor — especially one that returns |
Beta Was this translation helpful? Give feedback.
-
Ok. Thanks for the quick clarification! |
Beta Was this translation helpful? Give feedback.
-
I had a similar problem and solved with this pattern: # pyright: strict
from __future__ import annotations
from typing import reveal_type
class Foo[T]:
@classmethod
def from_value(cls, value: T) -> Foo[T]:
return cls(value)
def __init__(self, value: T) -> None:
self._value = value
a = Foo.from_value(1)
reveal_type(a) # Type of "a" is "Foo[int]" Unfortunately is not the same as using |
Beta Was this translation helpful? Give feedback.
A class method is bound to a specific class type. If that class is generic, the type arguments for the type are dictated by the (specialized) class type to which it is bound.
Unlike class methods, constructors are special-cased in the type system (and in type checkers). They determine the type arguments for a generic class at construction time even if the class being constructed is not explicitly specialized. Class methods are not like this. If you're using a class method as an alternative constructor — especially one that returns
Self
, you need to manually specify the specialized type of the class at the call site.