[{"data":1,"prerenderedAt":109},["ShallowReactive",2],{"topic-python-typing":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fpython.yml","Python interview questions across language fundamentals, data structures and common gotchas for backend, data and automation roles.","yml","python",{},"Python",3,"frameworks\u002Fpython",1,"QsijsotyAr-3rnhJDWWZmc7hE6HAhylS5t1dKpigMOA",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fpython-typing.yml","Type annotations, the typing module, generics and protocols — optional static typing for modern Python.",{},"Type Hints & Typing",11,"typing","topics\u002Fpython-typing","m9rEesQ-tmtEb8ibbPg4hXIEFkDaRfVfLdvDypVFl-g",[25,70],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"related":63,"seo":64,"seoDescription":65,"stem":66,"subtopic":67,"topic":19,"topicSlug":21,"updated":68,"__hash__":69},"qa\u002Fpython\u002Ftyping\u002Ftype-hints.md","Type Hints",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fpython\u002Ftyping\u002Ftype-hints",[41,46,50,54,59],{"id":42,"difficulty":43,"q":44,"a":45},"hints-runtime-enforced","easy","Are type hints enforced at runtime?","**No.** Type hints are **annotations**, not constraints — the interpreter\nstores them (in `__annotations__`) but **never checks them**. You can pass\na `str` where an `int` is annotated and Python runs it happily; enforcement\nis the job of an **external static type checker** like **mypy** or **pyright**.\n\n```python\ndef double(n: int) -> int:\n    return n * 2\n\ndouble(\"ab\")          # runs fine -> \"abab\", no TypeError\ndouble.__annotations__  # {'n': \u003Cclass 'int'>, 'return': \u003Cclass 'int'>}\n```\n\nIf you want runtime validation you opt in explicitly — e.g. **pydantic**,\n`typing.get_type_hints`, or manual `isinstance` checks. Rule of thumb: hints\ndocument and enable tooling; they are **not** a runtime guard.\n",{"id":47,"difficulty":35,"q":48,"a":49},"optional-union","What is the difference between Optional, Union, and the `|` operator?","`Union[A, B]` means \"**A or B**\". `Optional[X]` is just shorthand for\n`Union[X, None]` — a value that may be `X` **or** `None`. It does **not**\nmean \"optional argument\"; it means \"could be None\". Since Python 3.10 you\ncan write unions with the **`|` operator** instead of importing from `typing`.\n\n```python\nfrom typing import Optional, Union\n\ndef find(id: int) -> Optional[str]: ...     # str or None\ndef parse(x: Union[int, str]) -> int: ...   # int or str\n\n# Python 3.10+ equivalent, no imports:\ndef find(id: int) -> str | None: ...\ndef parse(x: int | str) -> int: ...\n```\n\nPrefer the modern `X | None` syntax on 3.10+. Reach for `Optional`\u002F`Union`\nfrom `typing` only when supporting older versions. Rule of thumb: `Optional`\nis about **nullability**, never about whether a parameter has a default.\n",{"id":51,"difficulty":35,"q":52,"a":53},"list-vs-generics","What is the difference between `list` and `List`, and how do generics work?","Both annotate a list, but **`List` comes from `typing`** while **`list`** is\nthe built-in. Since **Python 3.9** the built-in containers are themselves\n**subscriptable** (`list[int]`, `dict[str, int]`), so `typing.List`,\n`typing.Dict`, etc. are **deprecated** — use the lowercase built-ins. A bare\n`list` means \"list of anything\"; the **generic** form pins the element type.\n\n```python\nfrom typing import List          # legacy\nnames: List[str] = []\n\nnames: list[str] = []            # modern (3.9+), preferred\nscores: dict[str, int] = {}\npair: tuple[int, str] = (1, \"a\")\n```\n\nGenerics let a checker verify element access and method calls. Rule of thumb:\non 3.9+ always parameterize the **built-in** (`list[str]`), and only import\nfrom `typing` for things with no built-in equivalent (e.g. `Callable`).\n",{"id":55,"difficulty":56,"q":57,"a":58},"any-vs-object","hard","What is the difference between `typing.Any` and `object`?","Both accept any value, but they are **opposites to a type checker**. `object`\nis the real **base of every class** — you can assign anything to it, but you\ncan only do `object`-level operations on it. `Any` is an **escape hatch**: it\nis compatible with **everything in both directions**, so the checker stops\nchecking — any attribute or call is allowed.\n\n```python\ndef f(x: object) -> None:\n    x.upper()        # type error: object has no 'upper'\n\ndef g(x: Any) -> None:\n    x.upper()        # OK — Any disables checking\n    x + 1            # also OK, no complaints\n```\n\nUse `object` when you genuinely accept anything but want to **keep type\nsafety** (forcing you to narrow with `isinstance` first). Use `Any` only to\ndeliberately **opt out** of checking. Rule of thumb: `Any` is contagious and\nhides bugs — prefer `object` or a precise type.\n",{"id":60,"difficulty":35,"q":61,"a":62},"what-mypy-does","What does mypy do, and how is it different from Protocol-based typing?","**mypy** is a **static type checker**: it reads your annotations and flags\ntype mismatches **before you run the code** — no execution, no runtime cost.\nBy default it checks types **nominally** (by inheritance). `typing.Protocol`\nadds **structural typing** (a.k.a. duck typing): a class matches a Protocol\nif it has the right **methods\u002Fattributes**, even without inheriting from it.\n\n```python\nfrom typing import Protocol\n\nclass Closable(Protocol):\n    def close(self) -> None: ...\n\ndef shutdown(r: Closable) -> None:\n    r.close()\n\nclass File:                 # never imports\u002Finherits Closable\n    def close(self) -> None: ...\n\nshutdown(File())            # OK — File structurally matches\n```\n\nSo mypy verifies correctness, and `Protocol` lets it accept **anything with\nthe right shape** rather than a specific base class. Rule of thumb: use\nProtocols to type \"**anything that behaves like X**\" without forcing a\ncommon base class.\n",null,{"description":32},"Python interview questions on type hints, Optional and Union, generics with list vs List, typing.Any vs object, mypy, and Protocol structural typing.","python\u002Ftyping\u002Ftype-hints","Type Hints & Annotations","2026-06-18","-Huf_PnQ17R6ZD2DTYZJptZVH8wQAKCHGmNxduo3rC4",{"id":71,"title":72,"body":73,"description":32,"difficulty":56,"extension":36,"framework":10,"frameworkSlug":8,"meta":77,"navigation":38,"order":33,"path":78,"questions":79,"related":63,"seo":104,"seoDescription":105,"stem":106,"subtopic":107,"topic":19,"topicSlug":21,"updated":68,"__hash__":108},"qa\u002Fpython\u002Ftyping\u002Fgenerics-protocols.md","Generics Protocols",{"type":29,"value":74,"toc":75},[],{"title":32,"searchDepth":33,"depth":33,"links":76},[],{},"\u002Fpython\u002Ftyping\u002Fgenerics-protocols",[80,84,88,92,96,100],{"id":81,"difficulty":56,"q":82,"a":83},"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":85,"difficulty":56,"q":86,"a":87},"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":89,"difficulty":56,"q":90,"a":91},"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":93,"difficulty":35,"q":94,"a":95},"runtime-checkable","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":97,"difficulty":35,"q":98,"a":99},"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":101,"difficulty":56,"q":102,"a":103},"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",{"description":32},"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","tqB3wOAZgFxlWwfX7xtXC5pu1C_ZvRqITJYDLjrlBbc",1781808675671]