[{"data":1,"prerenderedAt":62},["ShallowReactive",2],{"qa-\u002Fpython\u002Ftyping\u002Fgenerics-protocols":3},{"page":4,"siblings":56,"blog":47},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"related":47,"seo":48,"seoDescription":49,"stem":50,"subtopic":51,"topic":52,"topicSlug":53,"updated":54,"__hash__":55},"qa\u002Fpython\u002Ftyping\u002Fgenerics-protocols.md","Generics Protocols",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","Python","python",{},true,"\u002Fpython\u002Ftyping\u002Fgenerics-protocols",[22,26,30,34,39,43],{"id":23,"difficulty":14,"q":24,"a":25},"typevar-generic","What are TypeVar and Generic, and how do you write a generic class?","A **`TypeVar`** is a type variable — a placeholder that lets a function or class\nwork with **any type while preserving it** across inputs and outputs. Subclassing\n**`Generic[T]`** turns a class into a generic container parameterized by that\nvariable, so a `Stack[int]` is known to hold and return `int`s.\n\n```python\nfrom typing import TypeVar, Generic\n\nT = TypeVar(\"T\")               # one placeholder type\n\ndef first(items: list[T]) -> T:  # in and out share T\n    return items[0]\n\nclass Stack(Generic[T]):       # generic class\n    def __init__(self) -> None:\n        self._items: list[T] = []\n    def push(self, x: T) -> None:\n        self._items.append(x)\n    def pop(self) -> T:\n        return self._items.pop()\n\ns: Stack[int] = Stack()\ns.push(1)\nn = s.pop()                    # type checker knows n is int\n```\n\nUse a `TypeVar` whenever a relationship between argument and return types must be\ncaptured — `def first(items: list) -> object` loses that, but `list[T] -> T`\nkeeps it. In Python 3.12+ you can write `def first[T](items: list[T]) -> T`\nwithout the explicit `TypeVar`.\n",{"id":27,"difficulty":14,"q":28,"a":29},"bounded-typevar","What is a bounded or constrained TypeVar?","A plain `TypeVar` accepts **any** type. A **bound** (`bound=...`) restricts it to a\ntype **and its subclasses**, while **constraints** (`TypeVar(\"T\", int, str)`)\nrestrict it to a fixed set of specific types. Both let the body safely use the\ncapabilities implied by the bound.\n\n```python\nfrom typing import TypeVar\n\nclass Animal:\n    def speak(self) -> str: ...\n\nA = TypeVar(\"A\", bound=Animal)     # A must be Animal or a subclass\n\ndef loudest(animals: list[A]) -> A:\n    for a in animals:\n        a.speak()                  # OK — bound guarantees this method\n    return animals[0]\n\nNum = TypeVar(\"Num\", int, float)   # constrained: ONLY int or float\ndef double(x: Num) -> Num:\n    return x * 2\n```\n\nReach for a **bound** when \"any subtype of X\" is acceptable and the body needs X's\ninterface; use **constraints** when only a handful of unrelated concrete types\nshould be allowed.\n",{"id":31,"difficulty":14,"q":32,"a":33},"protocol-structural","What is typing.Protocol and how does it enable structural typing?","A **`Protocol`** defines an interface by **shape** rather than inheritance: any\nobject that has the right methods\u002Fattributes is accepted, even if it never\nexplicitly subclasses the protocol. This is **structural typing** (\"duck typing\")\nchecked statically — \"if it walks like a duck.\"\n\n```python\nfrom typing import Protocol\n\nclass SupportsClose(Protocol):\n    def close(self) -> None: ...\n\ndef shutdown(resource: SupportsClose) -> None:\n    resource.close()\n\nclass File:                # never inherits SupportsClose...\n    def close(self) -> None: ...\n\nshutdown(File())           # ...but accepted: it has close()\n```\n\nContrast with **nominal typing** (the usual `class B(A)`), where you must declare the\nrelationship. Protocols decouple the consumer from concrete classes — great for\ntyping third-party objects you can't modify.\n",{"id":35,"difficulty":36,"q":37,"a":38},"runtime-checkable","medium","What does @runtime_checkable do for a Protocol?","By default a `Protocol` exists only for **static** checkers — `isinstance()` against\nit raises `TypeError`. Decorating it with **`@runtime_checkable`** allows\n`isinstance()` \u002F `issubclass()` checks at runtime, but only for the **presence of\nthe named methods**, not their signatures or return types.\n\n```python\nfrom typing import Protocol, runtime_checkable\n\n@runtime_checkable\nclass Sized(Protocol):\n    def __len__(self) -> int: ...\n\nisinstance([1, 2, 3], Sized)   # True  — list has __len__\nisinstance(42, Sized)          # False — int has no __len__\n```\n\nIt's a convenience, not a guarantee: the check confirms a method *exists*, not that\nit takes the right arguments. Prefer static checking; use `@runtime_checkable` only\nwhen you genuinely need a runtime branch.\n",{"id":40,"difficulty":36,"q":41,"a":42},"callable-types","How do you type a function passed as an argument?","Use **`Callable[[ArgTypes], ReturnType]`** from `typing` (or the built-in\n`collections.abc.Callable`). The first element is the **list of parameter types**,\nthe second is the **return type**. Use `...` for the parameters when you want to\naccept any signature.\n\n```python\nfrom collections.abc import Callable\n\ndef apply(fn: Callable[[int, int], int], a: int, b: int) -> int:\n    return fn(a, b)\n\napply(lambda x, y: x + y, 2, 3)        # 5\n\nhandler: Callable[..., None]           # any args, returns None\nno_args: Callable[[], str]             # takes nothing, returns str\n```\n\nFor more precise signatures (preserving exact parameters of a wrapped function),\n`ParamSpec` exists, but `Callable[[...], R]` covers the common cases. Type your\ncallbacks so the checker catches mismatched handlers.\n",{"id":44,"difficulty":14,"q":45,"a":46},"covariance-invariance","What is the difference between covariance and invariance in generics?","Variance describes whether `Container[Subtype]` is usable where `Container[Supertype]`\nis expected. **Invariant** (the default, e.g. `list[T]`): `list[int]` is **not** a\n`list[str]` *or* a `list[object]`. **Covariant**: `Tuple[int]` is acceptable as\n`Tuple[object]`. The intuition: **mutable** containers must be invariant for safety;\n**read-only** ones can be covariant.\n\n```python\ndef total(nums: list[float]) -> float: ...\nints: list[int] = [1, 2]\ntotal(ints)        # type ERROR — list is invariant\n\nfrom collections.abc import Sequence\ndef total2(nums: Sequence[float]) -> float: ...\ntotal2(ints)       # OK — Sequence is covariant (read-only)\n```\n\nWhy mutables are invariant: if `list[int]` were a `list[object]`, a function could\nappend a `str` to it, corrupting the original `list[int]`. Rule of thumb: accept\n**`Sequence`\u002F`Iterable`** (covariant, read-only) in parameters to be flexible; reserve\n`list`\u002F`dict` for when you truly need to mutate.\n",null,{"description":11},"Python interview questions on TypeVar and Generic classes, bounded type variables, typing.Protocol structural typing, Callable types, and covariance vs invariance.","python\u002Ftyping\u002Fgenerics-protocols","Generics & Protocols","Type Hints & Typing","typing","2026-06-18","tqB3wOAZgFxlWwfX7xtXC5pu1C_ZvRqITJYDLjrlBbc",[57,61],{"subtopic":58,"path":59,"order":60},"Type Hints & Annotations","\u002Fpython\u002Ftyping\u002Ftype-hints",1,{"subtopic":51,"path":20,"order":12},1781808682287]