Replies: 5 comments 2 replies
-
I can't think of any technical reason why this isn't allowed. Perhaps it's because class variable accesses typically don't include explicit class specialization. Using your example above, FWIW, pyright emits an error if you attempt to use a type variable within a |
Beta Was this translation helpful? Give feedback.
-
I forget that this limitation exists because it makes no sense WRT my mind-abstraction of Please provide solid reasoning or add the feature. |
Beta Was this translation helpful? Give feedback.
-
I'd like to throw my hat into the ring here, I'm not sure why this limitation exists, it seems inconsistent with the purpose of typings. Take for example this case where some abstract class View(ABC, Generic[Message, Result]):
# Limitation of the pep https://peps.python.org/pep-0526/#class-and-instance-variable-annotations
# see https://github.com/python/mypy/issues/5144#issuecomment-1001222212
# and https://github.com/python/typing/discussions/1424
accepts: ClassVar[Message] # type: ignore[misc]
returns: ClassVar[Result] # type: ignore[misc] The expected utilization might be something like class MyResult:
def __init__(self, result: list[dict]):
self.result = result
class MyView(View):
returns = MyResult
def serialize(self, result: list[dict]) -> MyResult:
return MyView.returns(result) I understand that if What steps would we need to take to have this limitation removed? It seems like a library such as |
Beta Was this translation helpful? Give feedback.
-
I believe the technical reason why this was not allowed is that different generic instantiations of the same class share a single runtime class object, which only has space for a single ClassVar of one type. You'd get type checker behavior like this: class Foo[T]:
x: ClassVar[T]
assert_type(Foo[int].x, int)
assert_type(Foo[str].x, str) But The practical use cases people come up with tend to involve generic base classes with non-generic child classes. That does make sense at runtime and in the type system, so it would be useful to allow. |
Beta Was this translation helpful? Give feedback.
-
In this scenario, you could create type hints for dynamic classes that have fixed class variables but do not affect other dynamic classes. from collections.abc import Callable
from types import new_class
from typing import ClassVar, Protocol, reveal_type
from pydantic import BaseModel
class DynamicClass[ModelT: BaseModel, T](Protocol):
model: ClassVar[type[ModelT]] # type: ignore
params: dict[str, object] # instance var
@staticmethod # ClassVar[Callable] needs @self
def handler(model: ModelT, /) -> T: ...
def __init__(self, params: dict[str, object]) -> None: ...
def callhandler(self) -> T: ...
def dynamic_init[T](
self: DynamicClass[BaseModel, T], params: dict[str, object]
) -> None:
self.params = params
def dynamic_callhandler[T](self: DynamicClass[BaseModel, T]) -> T:
return self.handler(self.model(**self.params))
class DynamicMeta(type):
model: type[BaseModel]
def dynamic_class[ModelT: BaseModel, T](
name: str,
model: type[ModelT],
) -> Callable[[Callable[[ModelT], T]], type[DynamicClass[ModelT, T]]]:
def inner(handler: Callable[[ModelT], T]) -> type[DynamicClass[ModelT, T]]:
return new_class(
name,
kwds={"metaclass": DynamicMeta},
exec_body=lambda ns: ns.update(
model=model,
handler=staticmethod(handler),
__init__=dynamic_init,
callhandler=dynamic_callhandler,
),
)
return inner
class FooModel(BaseModel):
value: object
@dynamic_class("Foo", FooModel)
def foo(model: FooModel) -> str:
return str(model.value)
inst = foo({"value": 5})
resp = inst.callhandler()
reveal_type(inst)
reveal_type(resp) mypy Revealed type is "DynamicClass[FooModel, str]"
Revealed type is "str" pyright Type of "inst" is "DynamicClass[FooModel, str]"
Type of "resp" is "str" at runtime
|
Beta Was this translation helpful? Give feedback.
-
I've read PEP 526 and searched around, but can't find any mention of why generics are not allowed in a
ClassVar
(just that they're not allowed). I was hoping to write a class like this:EDIT: I think I may have a misunderstanding of how to do what I want. I want to make a subclass like this:
class Bar(Foo[int]): ...
and useBar.Type
as a type annotation later on. Looks like this may not be possible?Beta Was this translation helpful? Give feedback.
All reactions