TypeVar, Generic Classes, and Protocol
Define reusable generic types and structural subtyping with Protocol.
TypeVar, Generic Classes, and Protocol is a free Python Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Python Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is TypeVar?
TypeVar declares a type variable — a placeholder that can be any type, constrained if needed. Used in generic functions and classes.
from typing import TypeVar
T = TypeVar("T")
def identity(value: T) -> T:
return value
print(identity(42)) # int
print(identity("hello")) # strGeneric Functions
A function using TypeVar in both parameter and return types allows mypy to infer the return type from the argument type.
from typing import TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T:
return items[0]
x: int = first([1, 2, 3]) # mypy knows x is int
s: str = first(["a","b","c"]) # mypy knows s is strConstrained TypeVar
Pass type constraints to TypeVar to restrict which types are acceptable.
from typing import TypeVar
Numeric = TypeVar("Numeric", int, float)
def double(n: Numeric) -> Numeric:
return n * 2
print(double(5)) # 10
print(double(2.5)) # 5.0
# double("x") # mypy errorGeneric Classes
Inherit from Generic[T] to create a class parameterised by a type variable.
from typing import Generic, TypeVar
T = TypeVar("T")
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
stack: Stack[int] = Stack()
stack.push(1)
print(stack.pop()) # 1Multiple TypeVars
Use multiple TypeVars to parameterise a class or function over two independent types.
from typing import Generic, TypeVar
K = TypeVar("K")
V = TypeVar("V")
class Pair(Generic[K, V]):
def __init__(self, key: K, value: V) -> None:
self.key = key
self.value = value
p: Pair[str, int] = Pair("age", 30)
print(p.key, p.value) # age 30Protocol — Structural Subtyping
Protocol defines an interface by the methods and attributes a class must have, without requiring explicit inheritance ("duck typing, type-checked").
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
def render(obj: Drawable) -> None:
obj.draw()
class Circle:
def draw(self) -> None:
print("Drawing circle")
render(Circle()) # works — Circle satisfies DrawableProtocol with runtime_checkable
Add @runtime_checkable to a Protocol to enable isinstance() checks at runtime.
from typing import Protocol, runtime_checkable
@runtime_checkable
class Closeable(Protocol):
def close(self) -> None: ...
class File:
def close(self) -> None: print("closed")
print(isinstance(File(), Closeable)) # TrueProtocol vs ABC
ABCs require explicit inheritance (class Dog(Animal)). Protocols work by structural matching — if a class has the right methods, it satisfies the protocol regardless of inheritance.
from typing import Protocol
from abc import ABC, abstractmethod
# ABC: nominal typing
class Printable(ABC):
@abstractmethod
def display(self) -> None: ...
# Protocol: structural typing
class Displayable(Protocol):
def display(self) -> None: ...TypeVar with bound
TypeVar("T", bound=BaseClass) restricts T to BaseClass or any of its subclasses.
from typing import TypeVar
class Animal:
def speak(self) -> str: return "..."
A = TypeVar("A", bound=Animal)
def make_speak(animal: A) -> str:
return animal.speak()Generic Alias — Python 3.12
Python 3.12 introduces type statement for clean type aliases and class Foo[T] syntax for generics without importing TypeVar.
# Python 3.12+
type Vector = list[float]
type Matrix = list[Vector]
class Stack[T]:
def __init__(self) -> None: self._items: list[T] = []
def push(self, item: T) -> None: self._items.append(item)
def pop(self) -> T: return self._items.pop()Self Type
Self (from typing in 3.11+) annotates methods that return the current class, useful in fluent interfaces and subclasses.
from typing import Self
class Builder:
def __init__(self) -> None:
self.parts: list[str] = []
def add(self, part: str) -> Self:
self.parts.append(part)
return self
b = Builder().add("a").add("b")
print(b.parts) # ["a", "b"]Quick Check
What is the key difference between a Protocol and an ABC in Python typing?
Recap
TypeVar creates type placeholders for generic functions and classes. Generic[T] parameterises classes. Protocol enables structural subtyping without inheritance. Use bound= or type constraints on TypeVar to restrict acceptable types.
Frequently asked questions
Is the “TypeVar, Generic Classes, and Protocol” lesson free?
Yes — the full text of “TypeVar, Generic Classes, and Protocol” is free to read here on the web, and the Python Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Python Academy course, upgrade to CoddyKit PRO.
What will I learn in “TypeVar, Generic Classes, and Protocol”?
Define reusable generic types and structural subtyping with Protocol. You practise Python Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Python Academy?
No prior experience is required. Python Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “TypeVar, Generic Classes, and Protocol” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Python Academy lesson?
Yes. Every Python Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Basic Type Annotations
- Complex Types: List, Dict, Optional, Union
- TypeVar, Generic Classes, and Protocol
- Running mypy and Fixing Type Errors