-
Notifications
You must be signed in to change notification settings - Fork 287
-
I don't think this is standard terminology. There may be a better way of describing what I'm talking about.
I'm seeing that Python generics are instance-level generics - that is, each instance could have a different type for the type variable:
CT = TypeVar("CT") class C(Generic[CT]): x: CT def __init__(self, x: CT) -> None: self.x = x def main() -> None: a = C[int](1) y = a.x + 1 print(y) b = C[str]("1") z = b.x + "1" print(z)
What I'm looking for is a way to specify that a subclass should specify a type that isn't specified by the base class.
Something like this:
PT = TypeVar("PT") class P(Generic[PT]): x: ClassVar[PT] # type error: "ClassVar" type cannot include type variables class Q(P[int]): x = 3 class R(P[str]): x = "three" def f(p: Type[P]) -> None: print(p.x) f(Q) f(R)
Is there any way to specify this to people subclassing my class?
Assuming this example code does what I want it to do, and represents it in a way I want, how can I specify the typing?
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 4 comments 4 replies
-
from typing import Generic, TypeVar
T = TypeVar('T')
class MetaP(type, Generic[T]):
x: T
class Foo(metaclass=MetaP[int]):
...
reveal_type(Foo.x) # Reveals int in pyright
Something like this maybe? A class variable is like an instance variable of a metaclass. Although different type checkers have different support for this. For pyright it works, for mypy generic metaclasses don't seem to work.
Beta Was this translation helpful? Give feedback.
All reactions
-
I'm getting Metaclass cannot be generic from Pylance. I don't know if it's a difference version of pyright.
Beta Was this translation helpful? Give feedback.
All reactions
-
Interesting. I'm unable to reproduce that with pyright/pylance. I tried pyright latest version and code type checks fine for me. pyright playground also type checks fine.
Beta Was this translation helpful? Give feedback.
All reactions
-
Whenever you are expecting a subclass to provide something, using abc.abstractmethod is probably the way to go:
https://mypy-play.net/?mypy=latest&python=3.11&gist=617d24caceb1069f05fd252ca2fe97d2
As you can see an abstract property with a setter can be provided by a ClassVar, since it provides the same interface. It maybe obfuscates the intent a bit and is a lot of boilerplate for one attribute, but it does work.
You may also wish to use the abc.ABC metaclass so the base class is considered abstract and comes with all the other benefits of abstract classes.
Beta Was this translation helpful? Give feedback.
All reactions
-
Actually it looks like this doesn't enforce the property being defined in subclasses, although I'm not sure why. I'm certain I managed to get abstract properties to work this way in the past.
Beta Was this translation helpful? Give feedback.
All reactions
-
Alright, mypy will only emit an error if you try to create an instance of a subclass with abstract properties/methods that have not been implemented. It will automatically consider the class abstract, regardless of whether it's using ABCMeta or not.
So technically this will catch the error, just not at the definition site of the subclass, but only once you attempt to use the subclass:
https://mypy-play.net/?mypy=latest&python=3.11&gist=8d87cfb3495d7cd17430edcf5d53eb26
Beta Was this translation helpful? Give feedback.
All reactions
-
Note that if you type ignore that error, mypy will still mostly do what you want: https://mypy-play.net/?mypy=latest&python=3.11&gist=fbe0cc42fcb250d60663d3e25567fa28
Beta Was this translation helpful? Give feedback.
All reactions
-
The type check will be lost.
python/mypy#11672
Beta Was this translation helpful? Give feedback.