You have understood Python generics wrong. A Generic, say Generic[T], makes the TypeVar T act as whatever you provide the instance with.

I found this page to be very useful with learning Generics.

Answer from sean-7777 on Stack Overflow
🌐
Python
typing.python.org › en › latest › spec › generics.html
Generics — typing documentation
This also makes T valid as a type within the class body. The Generic base class uses a metaclass that defines __getitem__ so that LoggedVar[t] is valid as a type:
Discussions

Specialized Generic typed class plus class inheritance
This is a continuation of question #1084 , adding class inheritance into the problem. Similar to previous example: _T = TypeVar('_T', str, int) class Klass(Generic[_T]): @overload def __new... More on github.com
🌐 github.com
1
1
February 19, 2022
When to make classes & functions generic in python?
I don't understand the, idunno, premise of the question, I guess? Firstly, narrowing the type of a function parameter (or more generally, anything that's contravariant) is not allowed. This is a violation of Liskov: class Parent: def func(self, x: int): ... class Child(Parent): def func(self, x: bool): ... And secondly, your 2nd example doesn't typecheck. The way you're using the TypeVar makes no sense - it's neither bound to a generic class, nor does it appear in the function signature more than once. It's really unclear to me what problem you're trying to solve. More on reddit.com
🌐 r/learnpython
7
1
August 29, 2023
Is it possible to inherit from class with Generic in Python? - Stack Overflow
Since Python 3.5 you can use Generics and other interesting stuff described in PEP-0484. I tried that and here's a code I have: from typing import TypeVar, Generic, Optional ... _T = TypeVar('T'... More on stackoverflow.com
🌐 stackoverflow.com
How to share type variables when inheriting from generic base classes? - Typing - Discussions on Python.org
My understanding is that the way to make a derived generic type without actually resolving the specifics of the type variable is to simply inherit from the base class and use the same type variable as the base class. This is a simplified example of doing that with a bound type variable: from ... More on discuss.python.org
🌐 discuss.python.org
2
January 30, 2025
🌐
Python.org
discuss.python.org › typing
Clarifying the rules for subclassing generic classes - Typing - Discussions on Python.org
October 30, 2024 - Currently, the spec has this to say about creating a generic class by subclassing another generic class in the pre-PEP 695 world: The Generic[T] base class is redundant in simple cases where you subclass some other generic class and specify type variables for its parameters: from typing import TypeVar from collections.abc import Iterator T = TypeVar('T') class MyIter(Iterator[T]): ... That class definition is equivalent to: class MyIter(Iterator[T], Generic[T]): ... It would be nice to cl...
🌐
Aemonge
aemonge.com › articles › python › types › diamond_issue.html
Python Generic Classes and Inheritance: Solving the Diamond Problem
December 28, 2024 - Here's an example demonstrating the use of `super()`: ```{python} class Base: def __init__(self, base_arg: Any) -> None: self.base_arg = base_arg class Left(Base): def __init__(self, left_arg: Any, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.left_arg = left_arg class Right(Base): def __init__(self, right_arg: Any, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.right_arg = right_arg class Diamond(Left, Right): def __init__(self, diamond_arg: Any, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.diamond_arg = di
🌐
Playful Python
playfulpython.com › python-type-hinting-generics-inheritance
Type Hinting: Generics & Inheritance
October 27, 2024 - The hello function above takes a Person class as argument and calls the greet method on it. We are calling the function, but passing in an Employee object instead of Person. Since Employee inherits Person, it also has the greet method and the code works.
🌐
Mypy
mypy.readthedocs.io › en › stable › generics.html
Generics - mypy 1.20.0 documentation
Here is the same example using the old syntax (required for Python 3.11 and earlier, but also supported on newer Python versions): from typing import TypeVar, Generic T = TypeVar('T') # Define type variable "T" class Stack(Generic[T]): def __init__(self) -> None: # Create an empty list with items of type T self.items: list[T] = [] def push(self, item: T) -> None: self.items.append(item) def pop(self) -> T: return self.items.pop() def empty(self) -> bool: return not self.items
Find elsewhere
🌐
ArjanCodes
arjancodes.com › blog › python-generics-tutorial
How to Use Python Generics Effectively | ArjanCodes
May 30, 2024 - Much of the code we write is reusable, ... in Python come into play. Generics allow you to write functions and classes that can operate on any data type while maintaining clear type expectations....
🌐
Python
bugs.python.org › issue38459
Issue 38459: typing: Classes that inherit `Generic[...]` indirectly aren't considered generic. - Python tracker
September 20, 2022 - This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/82640
🌐
Reddit
reddit.com › r/learnpython › when to make classes & functions generic in python?
r/learnpython on Reddit: When to make classes & functions generic in python?
August 29, 2023 -

I have been generally making my methods generic mostly for the following reasons:

  • I am subclassing an abstract class and want to override a method and narrow the type hinting in the arguments, which would otherwise violate the Liskov substitution principle

  • I am not subclassing/overriding, but would like return values, attributes, etc. of a class to be more narrow than the type hints it is currently bound to, since I may be using that class in many different places with different types.

In particular for the second case, I have realized that there are actually two approaches to tackle this:

  1. make the class generic and provide the type arguments for the instance type hints.

  2. do not make the class generic, but subclass simply for the purpose of updating the type hints

With projects I am working on, with "context" and "manager" classes, there can be many different attributes and methods with many different types (5+), hence, making the class generic on all of them is too verbose. If I do make a class generic, if any other attributes (containing instances of other classes) return those same generic types, I have to propagate the generic type down the entire chain when I am using composition instead of inheritance. This is something I would like to avoid. If I choose option 2, there would be an explosion of subclasses just to override the type hints.

When should I choose 1 or 2? Is there a better way to do this?

Option 1 example:

from typing import Self, TypeVar, Generic

BazT = TypeVar('BazT')

class Bar(Generic[BazT]):
    def method1(self: Self) -> BazT:
        ...

class Foo(Generic[BazT]):
    bar: Bar[BazT]
    
    def method1(self: Self, baz: BazT) -> None:
        ...
    
    def method2(self: Self) -> Bar[BazT]:
        ...

Option 2 example:

from typing import Self, Any, TypeVar

Baz = Any

class Bar:
    def method1(self: Self) -> Baz:
        ...

class Foo:
    bar: Bar
    
    def method1(self: Self, baz: Baz) -> None:
        ...
    
    def method2(self: Self) -> Bar:
        ...
        
BazNarrowed = TypeVar('BazNarrowed', bound=Baz) # (doesn't matter what this is just some more narrow type)

class BarSubclass(Bar):
    def method1(self: Self) -> BazNarrowed:
        ...

class FooSubclass(Foo):
    bar: BarSubclass
    
    def method1(self: Self, baz: BazNarrowed) -> None:
        ...
    
    def method2(self: Self) -> BarSubclass:
        ...
Top answer
1 of 3
2
I don't understand the, idunno, premise of the question, I guess? Firstly, narrowing the type of a function parameter (or more generally, anything that's contravariant) is not allowed. This is a violation of Liskov: class Parent: def func(self, x: int): ... class Child(Parent): def func(self, x: bool): ... And secondly, your 2nd example doesn't typecheck. The way you're using the TypeVar makes no sense - it's neither bound to a generic class, nor does it appear in the function signature more than once. It's really unclear to me what problem you're trying to solve.
2 of 3
1
Please roast me if I'm wrong on this, but this appears to be the exact situation for which TypeGuard in typing module was developed, as of Python 3.10. PEP-647 ~~~TypeGuard = typing.TypeGuard~~~ Special typing form used to annotate the return type of a user-defined type guard function. ``TypeGuard`` only accepts a single type argument. At runtime, functions marked this way should return a boolean. ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static type checkers to determine a more precise type of an expression within a program's code flow. Usually type narrowing is done by analyzing conditional code flow and applying the narrowing to a block of code. The conditional expression here is sometimes referred to as a "type guard". Sometimes it would be convenient to use a user-defined boolean function as a type guard. Such a function should use ``TypeGuard[...]`` as its return type to alert static type checkers to this intention. Using ``-> TypeGuard`` tells the static type checker that for a given function: 1. The return value is a boolean. 2. If the return value is ``True``, the type of its argument is the type inside ``TypeGuard``. For example:: def is_str(val: Union[str, float]): # "isinstance" type guard if isinstance(val, str): # Type of ``val`` is narrowed to ``str`` ... else: # Else, type of ``val`` is narrowed to ``float``. ... Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower form of ``TypeA`` (it can even be a wider form) and this may lead to type-unsafe results. The main reason is to allow for things like narrowing ``List[object]`` to ``List[str]`` even though the latter is not a subtype of the former, since ``List`` is invariant. The responsibility of writing type-safe type guards is left to the user. ``TypeGuard`` also works with type variables. For more information, see PEP 647 (User-Defined Type Guards).
🌐
Python.org
discuss.python.org › typing
How to share type variables when inheriting from generic base classes? - Typing - Discussions on Python.org
January 30, 2025 - My understanding is that the way to make a derived generic type without actually resolving the specifics of the type variable is to simply inherit from the base class and use the same type variable as the base class. This is a simplified example of doing that with a bound type variable: from typing import Generic, Sequence, TypeVar T = TypeVar("T", str, dict) class BaseGeneric(Generic[T]): def __init__(self, example: Sequence[T]): self._example: Sequence[T] = example class Child...
🌐
Python.org
discuss.python.org › python help
Python <3.9 Syntax for Inheriting Generic Type - Python Help - Discussions on Python.org
September 20, 2022 - I’m using Python 3.8, and I’ve seen: PyLance reports subscript will generate runtime exception within TYPE_CHECKING clause Type subscript runtime exception false positive What is the syntax for defining a class that inherits from a Generic type? I’ve tried the following: # Python 3.8 from __future__ import annotations from collections import OrderedDict from typing_extensions import TypeAlias od_alias: TypeAlias = 'OrderedDict[str, int]' class mydict(OrderedDict[str, int]): # Subscript ...
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
February 24, 2026 - A generic type is typically declared by adding a list of type parameters after the class name: class Mapping[KT, VT]: def __getitem__(self, key: KT) -> VT: ... # Etc. Such a class implicitly inherits from Generic.
🌐
GitHub
github.com › python › typing › issues › 85
What does it mean to inherit from a generic type without specifying type parameters? · Issue #85 · python/typing
April 14, 2015 - In the current implementation of typing.py the Iterator type is defined as follows: class Iterable(Generic[T_co], extra=collections_abc.Iterable): pass class Iterator(Iterable, extra=collections_abc.Iterator): # Note: No generic types he...
Author   vlasovskikh
Top answer
1 of 2
2

This was a bug in mypy that was fixed in mypy 0.700. As several people in the comments noted, that line of code validates fine in newer versions.

Note that in newer versions of mypy, the code in the question has a different problem:

main.py:8: error: Incompatible types in assignment (expression has type "None", variable has type "EntityId")
main.py:9: error: Incompatible types in assignment (expression has type "None", variable has type "StateType")

But that's outside the scope of the question and up to you to resolve however you'd like.

2 of 2
-3

I know this question is a little old, but just for future reference:

from __future__ import annotations
from typing import NamedTuple, Optional, Type


class UserState(NamedTuple):
    name: str
    age: int


class Entity:
    id: Optional[str]
    state: UserState

    @classmethod
    def from_state(cls: Type[Entity], state: UserState) -> Entity:
        entity_from_state: Entity = object.__new__(cls)
        entity_from_state.id = None
        entity_from_state.state = state
        return entity_from_state

    def assign_id(self, id: str) -> None:
        self.id = id


class User(Entity):
    def __init__(self, name: str, age: int) -> None:
        self.state = UserState(name=name, age=age)

    @property
    def name(self) -> str:
        return self.state.name

    @property
    def age(self) -> int:
        return self.state.age

    def have_birthday(self) -> None:
        new_age = self.state.age + 1
        self.state = self.state._replace(age=new_age)


# Create first object with constructor
u1 = User(name="Anders", age=47)

# Create second object from state
user_state = UserState(name="Hannes", age=27)
u2 = User.from_state(user_state)  # Line 47

print(u1.state)
print(u2.state)
🌐
Django
code.djangoproject.com › ticket › 33174
#33174 (Having a model inherit from Generic[T] breaks makemigrations) – Django
Python 3.12 introduces new syntax for generic classes without explicit inheritance from typing.Generic (​https://docs.python.org/3/whatsnew/3.12.html#pep-695-type-parameter-syntax), so having to trick Django by adding verbose TYPE_CHECKING conditionals and explicit Generic inheritance is a bummer.
🌐
Aaron Boman
aaronboman.com › programming › 2020 › 05 › 27 › python-3-and-generic-classes-for-ide-type-hinting
Python 3 and Generic Classes for IDE type-hinting – Aaron Boman
May 27, 2020 - The typing module that comes built-in ... to use this generic variable within a class, the Generic class must be imported and inherited within the base class members of the class....
🌐
GitHub
github.com › python › typing › issues › 41
Describe generic classes as base classes · Issue #41 · python/typing
January 15, 2015 - The PEP should probably mention what happens if a generic class is used as a base class. Examples to consider: class StrList(List[str]): ... # StrList is not generic T = TypeVar('T') class MyList(List[T]): ... # Mypy requires Generic[T] ...
Author   JukkaL
🌐
GitHub
github.com › samuelcolvin › pydantic › issues › 2380
Inheriting GenericModel does not make the subclass Generic · Issue #2380 · pydantic/pydantic
February 19, 2021 - Traceback (most recent call last): File "........./generic.py", line 28, in <module> c = MyOtherGenericModel[int](field=1) File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pydantic/generics.py", line 52, in __class_getitem__ check_parameters_count(cls, params) File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pydantic/generics.py", line 118, in check_parameters_count raise TypeError(f'Too {description} parameters for {cls.__name__}; actual {actual}, expected {expected}') TypeError: Too many parameters for MyOtherGenericModel; actual 1, expected 0 · The only workaround I've found is to use multiple inheritance and to keep adding Generic[T] as a second parent class on the child.
Author   phdowling