Generic type alias with ClassVar
Asked Answered
D

2

21

My type annotations I currently have look similar to the following, and I want to use an typing alias to not repeat myself so much:

A class has class variables, which can be either:

  • some specified type, or
  • a function (with a parameter) returning that same type.
class Foo(object):
  state:  ClassVar[Union[str, Callable[[SomeObject], str]]]  # str or func->str
  number: ClassVar[Union[int, Callable[[SomeObject], int]]]  # int or func->int
  foobar: ClassVar[Union[bool, Callable[[SomeObject], bool]]] # bool or func->bool
# end class

For completion sake, here is an example implementation:

class FooBar(Foo):
   state = "something"
   number = lambda x: int(x.bla)
   
   @classmethod
   def foobar(cls, x):
     return x.blabla == cls.state
   # end def
# end class

However I can't figure out how to make a subscriptable generic. I'm seeking for something kinda like:

ClassValueOrCallable = lambda T: ClassVar[Union[T, Callable[[SomeObject], T]]]

class Foo(object):
  state:  ClassValueOrCallable(str)
  number: ClassValueOrCallable(int)
  foobar: ClassValueOrCallable(bool)

Edit:
Following mypy's generic type aliases section, it seems to should be possible to write it as

T = TypeVar('T')  # Any type.
ClassValueOrCallable = ClassVar[Union[T, Callable[[SomeObject], T]]]

class Foo(object):
  state:  ClassValueOrCallable[str]
  number: ClassValueOrCallable[int]
  foobar: ClassValueOrCallable[bool]

But at least PyCharm doesn't recognise that, and just displays the type as Any, so I'm not too sure if it is correct. pycharm quick-docs showing 'Any'

Discriminate answered 26/5, 2019 at 21:14 Comment(1)
Does this answer your question? Can I make type aliases for type constructors in python using the typing module?Hescock
U
15

This should work:

from typing import *

T = TypeVar('T')  # Any type.
ValueOrCallable = Union[T, Callable[..., T]]

class Foo(object):
  state:  ClassVar[ValueOrCallable]

Ubana answered 16/8, 2019 at 10:36 Comment(2)
Pycharm agrees :)Fourhanded
But I can't seem to supply the str type hint, etc.Discriminate
L
0

Your attempts didn't work because of two reasons:

  • The type qualifier ClassVar cannot be used in type aliases; it must be used directly in the class body.
  • A call expression (e.g. identifier()) is not a valid annotation expression. In other words, it won't be recognized as a valid type hint by spec-compliant type checkers.

Thus, the type alias should be specified as:

# 3.12+
type ValueOrCallable[T] = T | Callable[[SomeObject], T]

# 3.10, 3.11
from typing import TypeAlias, TypeVar
T = TypeVar('T')
ValueOrCallable: TypeAlias = T | Callable[[SomeObject], T]

...and used as:

(playgrounds: Mypy, Pyright)

class Foo:
    state:  ClassVar[ValueOrCallable[str]]
    number: ClassVar[ValueOrCallable[int]]
    foobar: ClassVar[ValueOrCallable[bool]]
Limiting answered 30/7, 2024 at 5:31 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.