[{"data":1,"prerenderedAt":3424},["ShallowReactive",2],{"page-\u002Fpython\u002F20-type-hints-and-typing":3},{"id":4,"title":5,"body":6,"description":30,"extension":3418,"meta":3419,"navigation":88,"path":3420,"seo":3421,"stem":3422,"__hash__":3423},"content\u002Fpython\u002F20-type-hints-and-typing.md","20 — Type Hints & Typing",{"type":7,"value":8,"toc":3380},"minimark",[9,13,222,384,576,781,857,962,1067,1223,1372,1563,1769,1962,2036,2209,2382,2565,2691,2871,3070,3376],[10,11,5],"h1",{"id":12},"_20-type-hints-typing",[14,15,17,22,112,165],"question-wrapper",{"language":16},"python",[18,19,21],"h3",{"id":20},"q1-what-happens-when-the-following-code-is-run-with-plain-cpython-no-external-tool","Q1. What happens when the following code is run with plain CPython (no external tool)?",[23,24,25],"code-wrapper",{"language":16},[26,27,31],"pre",{"className":28,"code":29,"language":16,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark","def add(a: int, b: int) -> int:\n    return a + b\n\nprint(add(\"3\", \"4\"))\n","",[32,33,34,68,83,90],"code",{"__ignoreMap":30},[35,36,39,43,47,51,55,58,60,63,65],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"svdQ7","def",[35,44,46],{"class":45},"sIsaT"," add",[35,48,50],{"class":49},"ssxIu","(a: ",[35,52,54],{"class":53},"snvgF","int",[35,56,57],{"class":49},", b: ",[35,59,54],{"class":53},[35,61,62],{"class":49},") -> ",[35,64,54],{"class":53},[35,66,67],{"class":49},":\n",[35,69,71,74,77,80],{"class":37,"line":70},2,[35,72,73],{"class":41},"    return",[35,75,76],{"class":49}," a ",[35,78,79],{"class":41},"+",[35,81,82],{"class":49}," b\n",[35,84,86],{"class":37,"line":85},3,[35,87,89],{"emptyLinePlaceholder":88},true,"\n",[35,91,93,96,99,103,106,109],{"class":37,"line":92},4,[35,94,95],{"class":53},"print",[35,97,98],{"class":49},"(add(",[35,100,102],{"class":101},"sJ6F3","\"3\"",[35,104,105],{"class":49},", ",[35,107,108],{"class":101},"\"4\"",[35,110,111],{"class":49},"))\n",[113,114,117,136,146,156],"ul",{"className":115},[116],"contains-task-list",[118,119,122,126,127,130,131,133,134],"li",{"className":120},[121],"task-list-item",[123,124],"input",{"disabled":88,"type":125},"checkbox"," Raises a ",[32,128,129],{},"TypeError"," because ",[32,132,102],{}," is not an ",[32,135,54],{},[118,137,139,126,141,143,144],{"className":138},[121],[123,140],{"disabled":88,"type":125},[32,142,129],{}," because the return value doesn't match ",[32,145,54],{},[118,147,149,151,152,155],{"className":148},[121],[123,150],{"disabled":88,"type":125}," Prints ",[32,153,154],{},"\"34\""," with no error at all",[118,157,159,126,161,164],{"className":158},[121],[123,160],{"disabled":88,"type":125},[32,162,163],{},"SyntaxError"," at import time because the annotations are violated",[166,167,168,172,182],"details",{},[169,170,171],"summary",{},"Show Answer",[173,174,175,179,180,155],"p",{},[176,177,178],"strong",{},"Answer:"," C — Prints ",[32,181,154],{},[173,183,184,187,188,191,192,194,195,198,199,202,203,206,207,210,211,214,215,217,218,221],{},[176,185,186],{},"Explanation:"," ",[176,189,190],{},"Safety:"," Python's type hints are not enforced at runtime by the interpreter. ",[32,193,54],{}," on ",[32,196,197],{},"a"," and ",[32,200,201],{},"b"," is purely documentation\u002Fmetadata (stored in ",[32,204,205],{},"__annotations__",") unless a separate tool checks it. Since ",[32,208,209],{},"str + str"," is valid Python, ",[32,212,213],{},"add(\"3\", \"4\")"," just concatenates and returns ",[32,216,154],{},". The tempting answers assume CPython behaves like a statically typed language and raises on mismatched types — it does not; only static checkers like mypy or pyright, or runtime validators like ",[32,219,220],{},"pydantic",", would flag this.",[14,223,224,228,302,344],{"language":16},[18,225,227],{"id":226},"q2-which-two-type-hints-are-exactly-equivalent","Q2. Which two type hints are exactly equivalent?",[23,229,230],{"language":16},[26,231,233],{"className":28,"code":232,"language":16,"meta":30,"style":30},"from typing import Optional, Union\n\ndef f(x: Optional[str]) -> None: ...\ndef g(x: Union[str, None]) -> None: ...\n",[32,234,235,249,253,278],{"__ignoreMap":30},[35,236,237,240,243,246],{"class":37,"line":38},[35,238,239],{"class":41},"from",[35,241,242],{"class":49}," typing ",[35,244,245],{"class":41},"import",[35,247,248],{"class":49}," Optional, Union\n",[35,250,251],{"class":37,"line":70},[35,252,89],{"emptyLinePlaceholder":88},[35,254,255,257,260,263,266,269,272,275],{"class":37,"line":85},[35,256,42],{"class":41},[35,258,259],{"class":45}," f",[35,261,262],{"class":49},"(x: Optional[",[35,264,265],{"class":53},"str",[35,267,268],{"class":49},"]) -> ",[35,270,271],{"class":53},"None",[35,273,274],{"class":49},": ",[35,276,277],{"class":53},"...\n",[35,279,280,282,285,288,290,292,294,296,298,300],{"class":37,"line":92},[35,281,42],{"class":41},[35,283,284],{"class":45}," g",[35,286,287],{"class":49},"(x: Union[",[35,289,265],{"class":53},[35,291,105],{"class":49},[35,293,271],{"class":53},[35,295,268],{"class":49},[35,297,271],{"class":53},[35,299,274],{"class":49},[35,301,277],{"class":53},[113,303,305,318,327,338],{"className":304},[116],[118,306,308,310,311,314,315],{"className":307},[121],[123,309],{"disabled":88,"type":125}," They are equivalent — ",[32,312,313],{},"Optional[str]"," is defined as ",[32,316,317],{},"Union[str, None]",[118,319,321,323,324,326],{"className":320},[121],[123,322],{"disabled":88,"type":125}," They are different — ",[32,325,313],{}," also allows omitting the argument entirely",[118,328,330,323,332,334,335,337],{"className":329},[121],[123,331],{"disabled":88,"type":125},[32,333,317],{}," allows ",[32,336,54],{}," too, by widening",[118,339,341,343],{"className":340},[121],[123,342],{"disabled":88,"type":125}," They are equivalent only under Python 3.11+",[166,345,346,348,357],{},[169,347,171],{},[173,349,350,352,353,314,355],{},[176,351,178],{}," A — They are equivalent — ",[32,354,313],{},[32,356,317],{},[173,358,359,187,361,364,365,368,369,372,373,376,377,380,381,383],{},[176,360,186],{},[32,362,363],{},"Optional[X]"," is literally shorthand in the ",[32,366,367],{},"typing"," module for ",[32,370,371],{},"Union[X, None]"," — nothing more. A very common misconception (option B) is that ",[32,374,375],{},"Optional"," makes an argument optional in the sense of having a default value; it does not — you still must pass something explicitly unless you separately give the parameter a default like ",[32,378,379],{},"= None",". This equivalence has held since ",[32,382,375],{}," was introduced and is not a 3.11 change.",[14,385,386,398,468,518],{"language":16},[18,387,389,390,393,394,397],{"id":388},"q3-what-is-the-key-practical-difference-between-annotating-a-parameter-as-any-versus-object","Q3. What is the key practical difference between annotating a parameter as ",[32,391,392],{},"Any"," versus ",[32,395,396],{},"object","?",[23,399,400],{"language":16},[26,401,403],{"className":28,"code":402,"language":16,"meta":30,"style":30},"from typing import Any\n\ndef handle_any(x: Any) -> None:\n    x.whatever_method()\n\ndef handle_object(x: object) -> None:\n    x.whatever_method()\n",[32,404,405,416,420,434,439,444,463],{"__ignoreMap":30},[35,406,407,409,411,413],{"class":37,"line":38},[35,408,239],{"class":41},[35,410,242],{"class":49},[35,412,245],{"class":41},[35,414,415],{"class":49}," Any\n",[35,417,418],{"class":37,"line":70},[35,419,89],{"emptyLinePlaceholder":88},[35,421,422,424,427,430,432],{"class":37,"line":85},[35,423,42],{"class":41},[35,425,426],{"class":45}," handle_any",[35,428,429],{"class":49},"(x: Any) -> ",[35,431,271],{"class":53},[35,433,67],{"class":49},[35,435,436],{"class":37,"line":92},[35,437,438],{"class":49},"    x.whatever_method()\n",[35,440,442],{"class":37,"line":441},5,[35,443,89],{"emptyLinePlaceholder":88},[35,445,447,449,452,455,457,459,461],{"class":37,"line":446},6,[35,448,42],{"class":41},[35,450,451],{"class":45}," handle_object",[35,453,454],{"class":49},"(x: ",[35,456,396],{"class":53},[35,458,62],{"class":49},[35,460,271],{"class":53},[35,462,67],{"class":49},[35,464,466],{"class":37,"line":465},7,[35,467,438],{"class":49},[113,469,471,480,491,505],{"className":470},[116],[118,472,474,476,477],{"className":473},[121],[123,475],{"disabled":88,"type":125}," There is no difference — both disable all static type checking on ",[32,478,479],{},"x",[118,481,483,187,485,487,488,490],{"className":482},[121],[123,484],{"disabled":88,"type":125},[32,486,396],{}," disables checking, ",[32,489,392],{}," is checked strictly",[118,492,494,187,496,498,499,501,502,504],{"className":493},[121],[123,495],{"disabled":88,"type":125},[32,497,392],{}," tells the checker to skip checking on ",[32,500,479],{}," entirely; ",[32,503,396],{}," is a real type, so calling an arbitrary method on it is a static type error",[118,506,508,187,510,198,512,514,515,517],{"className":507},[121],[123,509],{"disabled":88,"type":125},[32,511,392],{},[32,513,396],{}," both restrict ",[32,516,479],{}," to have no methods at all",[166,519,520,522,533],{},[169,521,171],{},[173,523,524,526,527,498,529,501,531,504],{},[176,525,178],{}," C — ",[32,528,392],{},[32,530,479],{},[32,532,396],{},[173,534,535,187,537,187,540,542,543,545,546,548,549,105,552,555,556,559,560,563,564,566,567,569,570,572,573,575],{},[176,536,186],{},[176,538,539],{},"Idiom:",[32,541,396],{}," is the actual root of Python's type hierarchy — every value is an ",[32,544,396],{},", but a static checker only knows about the methods ",[32,547,396],{}," itself defines (",[32,550,551],{},"__eq__",[32,553,554],{},"__repr__",", etc.), so ",[32,557,558],{},"handle_object"," would fail a mypy check on ",[32,561,562],{},"x.whatever_method()",". ",[32,565,392],{}," is special-cased by type checkers to be compatible with everything in both directions, effectively opting ",[32,568,479],{}," out of static checking. Beginners often assume ",[32,571,396],{}," is the \"accept anything, no checks\" type since it's the base of everything — that's actually what ",[32,574,392],{}," is for.",[14,577,578,582,679,732],{"language":16},[18,579,581],{"id":580},"q4-what-does-this-generic-function-signature-guarantee-to-a-static-type-checker","Q4. What does this generic function signature guarantee to a static type checker?",[23,583,584],{"language":16},[26,585,587],{"className":28,"code":586,"language":16,"meta":30,"style":30},"from typing import TypeVar\n\nT = TypeVar(\"T\")\n\ndef first(items: list[T]) -> T:\n    return items[0]\n\nresult = first([1, 2, 3])\n",[32,588,589,600,604,621,625,635,648,652],{"__ignoreMap":30},[35,590,591,593,595,597],{"class":37,"line":38},[35,592,239],{"class":41},[35,594,242],{"class":49},[35,596,245],{"class":41},[35,598,599],{"class":49}," TypeVar\n",[35,601,602],{"class":37,"line":70},[35,603,89],{"emptyLinePlaceholder":88},[35,605,606,609,612,615,618],{"class":37,"line":85},[35,607,608],{"class":49},"T ",[35,610,611],{"class":41},"=",[35,613,614],{"class":49}," TypeVar(",[35,616,617],{"class":101},"\"T\"",[35,619,620],{"class":49},")\n",[35,622,623],{"class":37,"line":92},[35,624,89],{"emptyLinePlaceholder":88},[35,626,627,629,632],{"class":37,"line":441},[35,628,42],{"class":41},[35,630,631],{"class":45}," first",[35,633,634],{"class":49},"(items: list[T]) -> T:\n",[35,636,637,639,642,645],{"class":37,"line":446},[35,638,73],{"class":41},[35,640,641],{"class":49}," items[",[35,643,644],{"class":53},"0",[35,646,647],{"class":49},"]\n",[35,649,650],{"class":37,"line":465},[35,651,89],{"emptyLinePlaceholder":88},[35,653,655,658,660,663,666,668,671,673,676],{"class":37,"line":654},8,[35,656,657],{"class":49},"result ",[35,659,611],{"class":41},[35,661,662],{"class":49}," first([",[35,664,665],{"class":53},"1",[35,667,105],{"class":49},[35,669,670],{"class":53},"2",[35,672,105],{"class":49},[35,674,675],{"class":53},"3",[35,677,678],{"class":49},"])\n",[113,680,682,697,714,724],{"className":681},[116],[118,683,685,187,687,690,691,130,693,696],{"className":684},[121],[123,686],{"disabled":88,"type":125},[32,688,689],{},"result"," is inferred as ",[32,692,392],{},[32,694,695],{},"TypeVar"," can't track concrete types",[118,698,700,187,702,690,704,706,707,710,711,713],{"className":699},[121],[123,701],{"disabled":88,"type":125},[32,703,689],{},[32,705,54],{},", because the checker binds ",[32,708,709],{},"T"," to ",[32,712,54],{}," from the call site",[118,715,717,187,719,690,721],{"className":716},[121],[123,718],{"disabled":88,"type":125},[32,720,689],{},[32,722,723],{},"list[int]",[118,725,727,187,729,731],{"className":726},[121],[123,728],{"disabled":88,"type":125},[32,730,695],{}," forces a runtime check that all list items share the same type",[166,733,734,736,749],{},[169,735,171],{},[173,737,738,740,741,690,743,706,745,710,747,713],{},[176,739,178],{}," B — ",[32,742,689],{},[32,744,54],{},[32,746,709],{},[32,748,54],{},[173,750,751,753,754,756,757,105,759,761,762,764,765,768,769,771,772,774,775,777,778,780],{},[176,752,186],{}," A ",[32,755,695],{}," lets a static checker propagate a concrete type through a generic function: given ",[32,758,723],{},[32,760,709],{}," is bound to ",[32,763,54],{}," for that call, so the checker infers ",[32,766,767],{},"first(...)"," returns ",[32,770,54],{},". Nothing about this is enforced by CPython at runtime (option D is wrong — no runtime check ever happens); ",[32,773,695],{}," is a pure static-analysis construct. Option A is the common mistake of assuming generics degrade to ",[32,776,392],{},", when in fact the whole point of ",[32,779,695],{}," is to preserve the specific type through the call.",[14,782,783,791,832],{},[18,784,786,787,790],{"id":785},"q5-which-statement-correctly-describes-how-mypy-relates-to-running-your-program","Q5. Which statement correctly describes how ",[32,788,789],{},"mypy"," relates to running your program?",[113,792,794,805,813,824],{"className":793},[116],[118,795,797,187,799,801,802],{"className":796},[121],[123,798],{"disabled":88,"type":125},[32,800,789],{}," is a runtime import that Python executes automatically before ",[32,803,804],{},"main()",[118,806,808,187,810,812],{"className":807},[121],[123,809],{"disabled":88,"type":125},[32,811,789],{}," is a separate static-analysis tool you run against your source files; it reports type errors but does not change how the code executes",[118,814,816,187,818,820,821,823],{"className":815},[121],[123,817],{"disabled":88,"type":125},[32,819,789],{}," patches the CPython interpreter to raise ",[32,822,129],{}," on hint violations",[118,825,827,187,829,831],{"className":826},[121],[123,828],{"disabled":88,"type":125},[32,830,789],{}," and type hints are required for Python code to run at all",[166,833,834,836,842],{},[169,835,171],{},[173,837,838,740,840,812],{},[176,839,178],{},[32,841,789],{},[173,843,844,187,846,848,849,852,853,856],{},[176,845,186],{},[32,847,789],{}," (like ",[32,850,851],{},"pyright",") reads your source, checks annotations for consistency, and reports diagnostics — completely separately from ",[32,854,855],{},"python script.py",". It never modifies runtime behavior or the interpreter. Options A, C, and D describe a form of enforcement Python simply does not have out of the box; type hints are always optional documentation to CPython itself.",[14,858,859,863,906],{},[18,860,862],{"id":861},"q6-in-modern-python-39-which-is-the-recommended-way-to-annotate-a-list-of-integers-without-importing-anything-extra","Q6. In modern Python (3.9+), which is the recommended way to annotate \"a list of integers\" without importing anything extra?",[113,864,866,878,890,898],{"className":865},[116],[118,867,869,187,871,874,875,877],{"className":868},[121],[123,870],{"disabled":88,"type":125},[32,872,873],{},"List[int]"," from ",[32,876,367],{},", always",[118,879,881,187,883,885,886,889],{"className":880},[121],[123,882],{"disabled":88,"type":125},[32,884,723],{},", using the built-in ",[32,887,888],{},"list"," directly as a generic",[118,891,893,187,895],{"className":892},[121],[123,894],{"disabled":88,"type":125},[32,896,897],{},"list(int)",[118,899,901,187,903],{"className":900},[121],[123,902],{"disabled":88,"type":125},[32,904,905],{},"list\u003Cint>",[166,907,908,910,918],{},[169,909,171],{},[173,911,912,740,914,885,916,889],{},[176,913,178],{},[32,915,723],{},[32,917,888],{},[173,919,920,187,922,924,925,105,927,105,930,105,933,936,937,939,940,563,943,946,947,949,950,952,953,955,956,958,959,961],{},[176,921,186],{},[176,923,539],{}," PEP 585 (Python 3.9+) made the built-in collection types (",[32,926,888],{},[32,928,929],{},"dict",[32,931,932],{},"set",[32,934,935],{},"tuple",", etc.) directly subscriptable for annotations, so ",[32,938,723],{}," works without importing ",[32,941,942],{},"typing.List",[32,944,945],{},"typing.List[int]"," (option A) still works for backward compatibility but is now considered legacy style. ",[32,948,897],{}," (option C) is actually a runtime call to the ",[32,951,888],{}," constructor with ",[32,954,54],{}," as an argument — not an annotation at all, and it would raise ",[32,957,129],{}," if actually executed since ",[32,960,54],{}," isn't iterable.",[14,963,964,972,1030],{},[18,965,967,968,971],{"id":966},"q7-what-does-callableint-str-bool-describe","Q7. What does ",[32,969,970],{},"Callable[[int, str], bool]"," describe?",[113,973,975,989,1003,1016],{"className":974},[116],[118,976,978,980,981,105,983,985,986],{"className":977},[121],[123,979],{"disabled":88,"type":125}," A callable that takes any two positional arguments and returns either an ",[32,982,54],{},[32,984,265],{},", or ",[32,987,988],{},"bool",[118,990,992,994,995,997,998,1000,1001],{"className":991},[121],[123,993],{"disabled":88,"type":125}," A callable taking exactly one ",[32,996,54],{}," argument and one ",[32,999,265],{}," argument (positionally) and returning a ",[32,1002,988],{},[118,1004,1006,1008,1009,105,1011,1013,1014],{"className":1005},[121],[123,1007],{"disabled":88,"type":125}," A tuple of three types: ",[32,1010,54],{},[32,1012,265],{},", and ",[32,1015,988],{},[118,1017,1019,1021,1022,1024,1025,1027,1028],{"className":1018},[121],[123,1020],{"disabled":88,"type":125}," A callable that takes an ",[32,1023,54],{}," or ",[32,1026,265],{}," and returns ",[32,1029,988],{},[166,1031,1032,1034,1045],{},[169,1033,171],{},[173,1035,1036,1038,1039,997,1041,1000,1043],{},[176,1037,178],{}," B — A callable taking exactly one ",[32,1040,54],{},[32,1042,265],{},[32,1044,988],{},[173,1046,1047,187,1049,1052,1053,1055,1056,1059,1060,1062,1063,1066],{},[176,1048,186],{},[32,1050,1051],{},"Callable[[ArgTypes...], ReturnType]"," uses a list of the parameter types (positional-only, in order) as the first element and the return type as the second. So ",[32,1054,970],{}," means \"call it with ",[32,1057,1058],{},"(some_int, some_str)"," and get a ",[32,1061,988],{}," back.\" Option A misreads the union-like syntax that doesn't apply here; ",[32,1064,1065],{},"Callable","'s argument list is positional and ordered, not a set of alternatives.",[14,1068,1069,1080,1151,1195],{"language":16},[18,1070,1072,1073,1076,1077,397],{"id":1071},"q8-a-class-needs-to-reference-its-own-type-in-a-method-signature-before-the-class-body-finishes-executing-which-approach-avoids-a-nameerror-at-class-definition-time-on-python-versions-before-310-without-from-__future__-import-annotations","Q8. A class needs to reference its own type in a method signature before the class body finishes executing. Which approach avoids a ",[32,1074,1075],{},"NameError"," at class-definition time on Python versions before 3.10 without ",[32,1078,1079],{},"from __future__ import annotations",[23,1081,1082],{"language":16},[26,1083,1085],{"className":28,"code":1084,"language":16,"meta":30,"style":30},"class Node:\n    def __init__(self, value: int) -> None:\n        self.value = value\n        self.next: \"Node\" | None = None\n",[32,1086,1087,1097,1116,1129],{"__ignoreMap":30},[35,1088,1089,1092,1095],{"class":37,"line":38},[35,1090,1091],{"class":41},"class",[35,1093,1094],{"class":45}," Node",[35,1096,67],{"class":49},[35,1098,1099,1102,1105,1108,1110,1112,1114],{"class":37,"line":70},[35,1100,1101],{"class":41},"    def",[35,1103,1104],{"class":53}," __init__",[35,1106,1107],{"class":49},"(self, value: ",[35,1109,54],{"class":53},[35,1111,62],{"class":49},[35,1113,271],{"class":53},[35,1115,67],{"class":49},[35,1117,1118,1121,1124,1126],{"class":37,"line":85},[35,1119,1120],{"class":53},"        self",[35,1122,1123],{"class":49},".value ",[35,1125,611],{"class":41},[35,1127,1128],{"class":49}," value\n",[35,1130,1131,1133,1136,1139,1142,1145,1148],{"class":37,"line":92},[35,1132,1120],{"class":53},[35,1134,1135],{"class":49},".next: ",[35,1137,1138],{"class":101},"\"Node\"",[35,1140,1141],{"class":41}," |",[35,1143,1144],{"class":53}," None",[35,1146,1147],{"class":41}," =",[35,1149,1150],{"class":53}," None\n",[113,1152,1154,1163,1176,1185],{"className":1153},[116],[118,1155,1157,1159,1160,1162],{"className":1156},[121],[123,1158],{"disabled":88,"type":125}," It will always raise ",[32,1161,1075],{}," regardless of quoting",[118,1164,1166,1168,1169,1171,1172,1175],{"className":1165},[121],[123,1167],{"disabled":88,"type":125}," Quoting the type as a string forward reference (",[32,1170,1138],{},") defers evaluation so it works even though ",[32,1173,1174],{},"Node"," isn't fully defined yet",[118,1177,1179,1181,1182,1184],{"className":1178},[121],[123,1180],{"disabled":88,"type":125}," You must define ",[32,1183,1174],{}," twice — once as a stub, once for real",[118,1186,1188,1190,1191,1194],{"className":1187},[121],[123,1189],{"disabled":88,"type":125}," Forward references only work inside ",[32,1192,1193],{},"@dataclass"," classes",[166,1196,1197,1199,1208],{},[169,1198,171],{},[173,1200,1201,1203,1204,1171,1206,1175],{},[176,1202,178],{}," B — Quoting the type as a string forward reference (",[32,1205,1138],{},[32,1207,1174],{},[173,1209,1210,187,1212,1215,1216,1218,1219,1222],{},[176,1211,186],{},[176,1213,1214],{},"Debug:"," Annotations are normally evaluated at function-definition time by default in older Python, so referencing the enclosing class by name before its ",[32,1217,1091],{}," statement completes would fail. Wrapping the reference in quotes makes it a forward reference — a string that a type checker parses lazily instead of the interpreter evaluating it immediately. Note the snippet mixes syntaxes for illustration (the ",[32,1220,1221],{},"\"Node\" | None"," union-with-string form needs care), but the core mechanism being tested is that string annotations sidestep the ordering problem, unlike option A's assumption that it always fails.",[14,1224,1225,1233,1303,1343],{"language":16},[18,1226,1228,1229,1232],{"id":1227},"q9-why-would-a-codebase-use-if-type_checking-around-an-import","Q9. Why would a codebase use ",[32,1230,1231],{},"if TYPE_CHECKING:"," around an import?",[23,1234,1235],{"language":16},[26,1236,1238],{"className":28,"code":1237,"language":16,"meta":30,"style":30},"from typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from mymodule import HeavyClass\n\ndef process(item: \"HeavyClass\") -> None: ...\n",[32,1239,1240,1251,1255,1265,1278,1282],{"__ignoreMap":30},[35,1241,1242,1244,1246,1248],{"class":37,"line":38},[35,1243,239],{"class":41},[35,1245,242],{"class":49},[35,1247,245],{"class":41},[35,1249,1250],{"class":53}," TYPE_CHECKING\n",[35,1252,1253],{"class":37,"line":70},[35,1254,89],{"emptyLinePlaceholder":88},[35,1256,1257,1260,1263],{"class":37,"line":85},[35,1258,1259],{"class":41},"if",[35,1261,1262],{"class":53}," TYPE_CHECKING",[35,1264,67],{"class":49},[35,1266,1267,1270,1273,1275],{"class":37,"line":92},[35,1268,1269],{"class":41},"    from",[35,1271,1272],{"class":49}," mymodule ",[35,1274,245],{"class":41},[35,1276,1277],{"class":49}," HeavyClass\n",[35,1279,1280],{"class":37,"line":441},[35,1281,89],{"emptyLinePlaceholder":88},[35,1283,1284,1286,1289,1292,1295,1297,1299,1301],{"class":37,"line":446},[35,1285,42],{"class":41},[35,1287,1288],{"class":45}," process",[35,1290,1291],{"class":49},"(item: ",[35,1293,1294],{"class":101},"\"HeavyClass\"",[35,1296,62],{"class":49},[35,1298,271],{"class":53},[35,1300,274],{"class":49},[35,1302,277],{"class":53},[113,1304,1306,1312,1325,1337],{"className":1305},[116],[118,1307,1309,1311],{"className":1308},[121],[123,1310],{"disabled":88,"type":125}," To make the import faster by caching it",[118,1313,1315,187,1317,1320,1321,1324],{"className":1314},[121],[123,1316],{"disabled":88,"type":125},[32,1318,1319],{},"TYPE_CHECKING"," is always ",[32,1322,1323],{},"True",", so this is identical to a normal import",[118,1326,1328,187,1330,1332,1333,1336],{"className":1327},[121],[123,1329],{"disabled":88,"type":125},[32,1331,1319],{}," is ",[32,1334,1335],{},"False"," at runtime, so the import (which might cause a circular import or unnecessary runtime cost) is skipped when the program actually executes, while static checkers still see it and validate the annotation",[118,1338,1340,1342],{"className":1339},[121],[123,1341],{"disabled":88,"type":125}," It silences all mypy errors in the module",[166,1344,1345,1347,1356],{},[169,1346,171],{},[173,1348,1349,526,1351,1332,1353,1355],{},[176,1350,178],{},[32,1352,1319],{},[32,1354,1335],{}," at runtime, so the import is skipped when the program executes, while static checkers still see it and validate the annotation",[173,1357,1358,187,1360,1332,1363,1365,1366,1368,1369,1371],{},[176,1359,186],{},[32,1361,1362],{},"typing.TYPE_CHECKING",[32,1364,1335],{}," during normal interpretation and treated as ",[32,1367,1323],{}," by static analyzers like mypy. This lets you import a class purely for annotation purposes — avoiding a circular import or a heavy\u002Foptional dependency at runtime — while the checker still resolves the forward-referenced type string. Assuming it's always ",[32,1370,1323],{}," (option B) would mean the import always executes, defeating the entire purpose of the pattern.",[14,1373,1374,1382,1458,1511],{"language":16},[18,1375,1377,1378,1381],{"id":1376},"q10-what-does-userid-newtypeuserid-int-actually-produce-at-runtime","Q10. What does ",[32,1379,1380],{},"UserId = NewType(\"UserId\", int)"," actually produce at runtime?",[23,1383,1384],{"language":16},[26,1385,1387],{"className":28,"code":1386,"language":16,"meta":30,"style":30},"from typing import NewType\n\nUserId = NewType(\"UserId\", int)\nuid = UserId(42)\nprint(type(uid), uid + 1)\n",[32,1388,1389,1400,1404,1423,1438],{"__ignoreMap":30},[35,1390,1391,1393,1395,1397],{"class":37,"line":38},[35,1392,239],{"class":41},[35,1394,242],{"class":49},[35,1396,245],{"class":41},[35,1398,1399],{"class":49}," NewType\n",[35,1401,1402],{"class":37,"line":70},[35,1403,89],{"emptyLinePlaceholder":88},[35,1405,1406,1409,1411,1414,1417,1419,1421],{"class":37,"line":85},[35,1407,1408],{"class":49},"UserId ",[35,1410,611],{"class":41},[35,1412,1413],{"class":49}," NewType(",[35,1415,1416],{"class":101},"\"UserId\"",[35,1418,105],{"class":49},[35,1420,54],{"class":53},[35,1422,620],{"class":49},[35,1424,1425,1428,1430,1433,1436],{"class":37,"line":92},[35,1426,1427],{"class":49},"uid ",[35,1429,611],{"class":41},[35,1431,1432],{"class":49}," UserId(",[35,1434,1435],{"class":53},"42",[35,1437,620],{"class":49},[35,1439,1440,1442,1445,1448,1451,1453,1456],{"class":37,"line":441},[35,1441,95],{"class":53},[35,1443,1444],{"class":49},"(",[35,1446,1447],{"class":53},"type",[35,1449,1450],{"class":49},"(uid), uid ",[35,1452,79],{"class":41},[35,1454,1455],{"class":53}," 1",[35,1457,620],{"class":49},[113,1459,1461,1474,1489,1501],{"className":1460},[116],[118,1462,1464,1466,1467,1469,1470,1473],{"className":1463},[121],[123,1465],{"disabled":88,"type":125}," A new subclass of ",[32,1468,54],{}," with ",[32,1471,1472],{},"UserId"," as its runtime type",[118,1475,1477,1479,1480,1332,1483,1013,1485,1488],{"className":1476},[121],[123,1478],{"disabled":88,"type":125}," A callable that at runtime just returns its argument unchanged — ",[32,1481,1482],{},"type(uid)",[32,1484,54],{},[32,1486,1487],{},"uid + 1"," works as plain int arithmetic; the distinctness only exists for static checkers",[118,1490,1492,753,1494,1496,1497,1500],{"className":1491},[121],[123,1493],{"disabled":88,"type":125},[32,1495,129],{},", because ",[32,1498,1499],{},"NewType"," requires a class, not a builtin",[118,1502,1504,1506,1507,1510],{"className":1503},[121],[123,1505],{"disabled":88,"type":125}," A dataclass wrapping the int with a ",[32,1508,1509],{},".value"," attribute",[166,1512,1513,1515,1520],{},[169,1514,171],{},[173,1516,1517,1519],{},[176,1518,178],{}," B — A callable that at runtime just returns its argument unchanged; the distinctness only exists for static checkers",[173,1521,1522,187,1524,187,1526,1528,1529,1532,1533,1013,1535,1537,1538,1541,1542,1544,1545,1547,1548,1550,1551,1553,1554,1556,1557,1559,1560,1562],{},[176,1523,186],{},[176,1525,539],{},[32,1527,1499],{}," creates a lightweight identity function at runtime — calling ",[32,1530,1531],{},"UserId(42)"," just returns ",[32,1534,1435],{},[32,1536,1482],{}," prints ",[32,1539,1540],{},"\u003Cclass 'int'>",", not some special ",[32,1543,1472],{}," type. Its entire purpose is to let a static checker treat ",[32,1546,1472],{}," and plain ",[32,1549,54],{}," as distinct (so you can't accidentally pass a raw ",[32,1552,54],{}," where a ",[32,1555,1472],{}," is expected) without any runtime wrapping cost. Believing it creates a real subclass (option A) is the most common misunderstanding — that's what subclassing ",[32,1558,54],{}," directly would do, not ",[32,1561,1499],{},".",[14,1564,1565,1573,1667,1727],{"language":16},[18,1566,1568,1569,1572],{"id":1567},"q11-what-runtime-guarantee-does-a-typeddict-provide","Q11. What runtime guarantee does a ",[32,1570,1571],{},"TypedDict"," provide?",[23,1574,1575],{"language":16},[26,1576,1578],{"className":28,"code":1577,"language":16,"meta":30,"style":30},"from typing import TypedDict\n\nclass Movie(TypedDict):\n    title: str\n    year: int\n\nm: Movie = {\"title\": \"Arrival\", \"year\": \"2016\"}\nprint(m)\n",[32,1579,1580,1591,1595,1609,1617,1625,1629,1660],{"__ignoreMap":30},[35,1581,1582,1584,1586,1588],{"class":37,"line":38},[35,1583,239],{"class":41},[35,1585,242],{"class":49},[35,1587,245],{"class":41},[35,1589,1590],{"class":49}," TypedDict\n",[35,1592,1593],{"class":37,"line":70},[35,1594,89],{"emptyLinePlaceholder":88},[35,1596,1597,1599,1602,1604,1606],{"class":37,"line":85},[35,1598,1091],{"class":41},[35,1600,1601],{"class":45}," Movie",[35,1603,1444],{"class":49},[35,1605,1571],{"class":45},[35,1607,1608],{"class":49},"):\n",[35,1610,1611,1614],{"class":37,"line":92},[35,1612,1613],{"class":49},"    title: ",[35,1615,1616],{"class":53},"str\n",[35,1618,1619,1622],{"class":37,"line":441},[35,1620,1621],{"class":49},"    year: ",[35,1623,1624],{"class":53},"int\n",[35,1626,1627],{"class":37,"line":446},[35,1628,89],{"emptyLinePlaceholder":88},[35,1630,1631,1634,1636,1639,1642,1644,1647,1649,1652,1654,1657],{"class":37,"line":465},[35,1632,1633],{"class":49},"m: Movie ",[35,1635,611],{"class":41},[35,1637,1638],{"class":49}," {",[35,1640,1641],{"class":101},"\"title\"",[35,1643,274],{"class":49},[35,1645,1646],{"class":101},"\"Arrival\"",[35,1648,105],{"class":49},[35,1650,1651],{"class":101},"\"year\"",[35,1653,274],{"class":49},[35,1655,1656],{"class":101},"\"2016\"",[35,1658,1659],{"class":49},"}\n",[35,1661,1662,1664],{"class":37,"line":654},[35,1663,95],{"class":53},[35,1665,1666],{"class":49},"(m)\n",[113,1668,1670,1690,1704,1717],{"className":1669},[116],[118,1671,1673,1675,1676,1679,1680,1682,1683,1686,1687,1689],{"className":1672},[121],[123,1674],{"disabled":88,"type":125}," None — at runtime ",[32,1677,1678],{},"m"," is a plain ",[32,1681,929],{},"; the ",[32,1684,1685],{},"\"year\": \"2016\""," string instead of ",[32,1688,54],{}," will not raise anything, and this only shows up as an error under a static checker",[118,1691,1693,753,1695,1697,1698,1701,1702],{"className":1692},[121],[123,1694],{"disabled":88,"type":125},[32,1696,129],{}," is raised immediately because ",[32,1699,1700],{},"year"," should be an ",[32,1703,54],{},[118,1705,1707,187,1709,1711,1712,710,1714],{"className":1706},[121],[123,1708],{"disabled":88,"type":125},[32,1710,1571],{}," automatically coerces ",[32,1713,1656],{},[32,1715,1716],{},"2016",[118,1718,1720,1722,1723,1726],{"className":1719},[121],[123,1721],{"disabled":88,"type":125}," Missing required keys raise a ",[32,1724,1725],{},"KeyError"," at construction time",[166,1728,1729,1731,1741],{},[169,1730,171],{},[173,1732,1733,1735,1736,1679,1738,1740],{},[176,1734,178],{}," A — None — at runtime ",[32,1737,1678],{},[32,1739,929],{},"; the string year does not raise, and only a static checker flags it",[173,1742,1743,187,1745,1747,1748,1750,1751,1754,1755,1757,1758,1760,1761,1763,1764,1766,1767,1562],{},[176,1744,186],{},[32,1746,1571],{}," exists purely to give static checkers shape information about a ",[32,1749,929],{},"'s expected keys and value types; at runtime, ",[32,1752,1753],{},"Movie"," instances are ordinary ",[32,1756,929],{}," objects with zero validation, coercion, or enforcement. Both the type mismatch (",[32,1759,1656],{}," vs ",[32,1762,54],{},") and even a missing required key would run without error — only tools like mypy catch them. For actual runtime validation, you'd reach for something like ",[32,1765,220],{}," or manual checks, not ",[32,1768,1571],{},[14,1770,1771,1779,1878,1924],{"language":16},[18,1772,1774,1775,1778],{"id":1773},"q12-what-happens-if-you-call-a-function-decorated-with-multiple-overload-signatures-using-arguments-that-dont-match-any-of-the-declared-overloads-when-running-the-actual-script-not-a-type-checker","Q12. What happens if you call a function decorated with multiple ",[32,1776,1777],{},"@overload"," signatures using arguments that don't match any of the declared overloads, when running the actual script (not a type checker)?",[23,1780,1781],{"language":16},[26,1782,1784],{"className":28,"code":1783,"language":16,"meta":30,"style":30},"from typing import overload\n\n@overload\ndef process(x: int) -> int: ...\n@overload\ndef process(x: str) -> str: ...\ndef process(x):\n    return x\n\nprocess(3.14)\n",[32,1785,1786,1797,1801,1806,1824,1828,1846,1855,1862,1867],{"__ignoreMap":30},[35,1787,1788,1790,1792,1794],{"class":37,"line":38},[35,1789,239],{"class":41},[35,1791,242],{"class":49},[35,1793,245],{"class":41},[35,1795,1796],{"class":49}," overload\n",[35,1798,1799],{"class":37,"line":70},[35,1800,89],{"emptyLinePlaceholder":88},[35,1802,1803],{"class":37,"line":85},[35,1804,1805],{"class":45},"@overload\n",[35,1807,1808,1810,1812,1814,1816,1818,1820,1822],{"class":37,"line":92},[35,1809,42],{"class":41},[35,1811,1288],{"class":45},[35,1813,454],{"class":49},[35,1815,54],{"class":53},[35,1817,62],{"class":49},[35,1819,54],{"class":53},[35,1821,274],{"class":49},[35,1823,277],{"class":53},[35,1825,1826],{"class":37,"line":441},[35,1827,1805],{"class":45},[35,1829,1830,1832,1834,1836,1838,1840,1842,1844],{"class":37,"line":446},[35,1831,42],{"class":41},[35,1833,1288],{"class":45},[35,1835,454],{"class":49},[35,1837,265],{"class":53},[35,1839,62],{"class":49},[35,1841,265],{"class":53},[35,1843,274],{"class":49},[35,1845,277],{"class":53},[35,1847,1848,1850,1852],{"class":37,"line":465},[35,1849,42],{"class":41},[35,1851,1288],{"class":45},[35,1853,1854],{"class":49},"(x):\n",[35,1856,1857,1859],{"class":37,"line":654},[35,1858,73],{"class":41},[35,1860,1861],{"class":49}," x\n",[35,1863,1865],{"class":37,"line":1864},9,[35,1866,89],{"emptyLinePlaceholder":88},[35,1868,1870,1873,1876],{"class":37,"line":1869},10,[35,1871,1872],{"class":49},"process(",[35,1874,1875],{"class":53},"3.14",[35,1877,620],{"class":49},[113,1879,1881,1894,1906,1915],{"className":1880},[116],[118,1882,1884,1886,1887,1889,1890,1893],{"className":1883},[121],[123,1885],{"disabled":88,"type":125}," Raises ",[32,1888,129],{}," immediately because ",[32,1891,1892],{},"float"," matches no overload",[118,1895,1897,1899,1900,1902,1903,1905],{"className":1896},[121],[123,1898],{"disabled":88,"type":125}," Runs fine at runtime — only the final, un-decorated implementation actually executes, and ",[32,1901,1777],{}," stubs are ignored by the interpreter; only a static checker would flag the ",[32,1904,1892],{}," argument as invalid",[118,1907,1909,1911,1912,1914],{"className":1908},[121],[123,1910],{"disabled":88,"type":125}," Silently returns ",[32,1913,271],{}," because no overload matched",[118,1916,1918,1920,1921,1923],{"className":1917},[121],[123,1919],{"disabled":88,"type":125}," The ",[32,1922,1777],{}," decorator dispatches to the closest matching stub automatically",[166,1925,1926,1928,1936],{},[169,1927,171],{},[173,1929,1930,1932,1933,1935],{},[176,1931,178],{}," B — Runs fine at runtime; ",[32,1934,1777],{}," stubs are ignored by the interpreter and only the real implementation executes",[173,1937,1938,187,1940,187,1942,1944,1945,1948,1949,1952,1953,1955,1956,1532,1959,1961],{},[176,1939,186],{},[176,1941,1214],{},[32,1943,1777],{},"-decorated stub bodies (the ",[32,1946,1947],{},"..."," ones) are never actually called — Python discards them, and only the final non-decorated ",[32,1950,1951],{},"process(x): return x"," implementation runs. ",[32,1954,1777],{}," exists solely so a static checker can offer precise per-signature type checking and autocompletion; there is no runtime dispatch mechanism at all (option D is a common false assumption — Python has no built-in multiple dispatch). Since the concrete implementation accepts anything, ",[32,1957,1958],{},"process(3.14)",[32,1960,1875],{}," with no error.",[14,1963,1964,1976,2015],{},[18,1965,1967,1968,1971,1972,1975],{"id":1966},"q13-a-function-is-annotated-def-get_ids-listint-but-its-body-is-return-is-this-a-problem-at-runtime","Q13. A function is annotated ",[32,1969,1970],{},"def get_ids() -> list[int]:"," but its body is ",[32,1973,1974],{},"return []",". Is this a problem at runtime?",[113,1977,1979,1991,2000,2006],{"className":1978},[116],[118,1980,1982,1984,1985,1987,1988,1990],{"className":1981},[121],[123,1983],{"disabled":88,"type":125}," Yes — an empty list can never satisfy ",[32,1986,723],{},", so this raises ",[32,1989,129],{}," on return",[118,1992,1994,1996,1997,1999],{"className":1993},[121],[123,1995],{"disabled":88,"type":125}," No — an empty list contains no elements to contradict ",[32,1998,54],{},", and since hints aren't enforced at runtime anyway, this runs without any error regardless",[118,2001,2003,2005],{"className":2002},[121],[123,2004],{"disabled":88,"type":125}," Yes, but only in Python 3.12+",[118,2007,2009,2011,2012],{"className":2008},[121],[123,2010],{"disabled":88,"type":125}," No, but it silently converts to ",[32,2013,2014],{},"[0]",[166,2016,2017,2019,2024],{},[169,2018,171],{},[173,2020,2021,2023],{},[176,2022,178],{}," B — No; an empty list vacuously satisfies the hint, and runtime never checks it anyway",[173,2025,2026,2028,2029,2031,2032,2035],{},[176,2027,186],{}," Two separate reasons both point to \"no error\": logically, ",[32,2030,723],{}," is satisfied vacuously by an empty list since there's no element violating the constraint, and practically, CPython never inspects the return annotation at runtime regardless. Even returning ",[32,2033,2034],{},"[\"not\", \"an\", \"int\"]"," from this function would run without any exception — the mismatch would only surface as a static type error under mypy.",[14,2037,2038,2046,2108,2165],{"language":16},[18,2039,2041,2042,2045],{"id":2040},"q14-what-is-the-correct-type-hint-safe-way-to-give-a-dataclass-field-a-mutable-default-value","Q14. What is the correct, type-hint-safe way to give a ",[32,2043,2044],{},"dataclass"," field a mutable default value?",[23,2047,2048],{"language":16},[26,2049,2051],{"className":28,"code":2050,"language":16,"meta":30,"style":30},"from dataclasses import dataclass, field\n\n@dataclass\nclass Config:\n    tags: list[str] = field(default_factory=list)\n",[32,2052,2053,2065,2069,2074,2083],{"__ignoreMap":30},[35,2054,2055,2057,2060,2062],{"class":37,"line":38},[35,2056,239],{"class":41},[35,2058,2059],{"class":49}," dataclasses ",[35,2061,245],{"class":41},[35,2063,2064],{"class":49}," dataclass, field\n",[35,2066,2067],{"class":37,"line":70},[35,2068,89],{"emptyLinePlaceholder":88},[35,2070,2071],{"class":37,"line":85},[35,2072,2073],{"class":45},"@dataclass\n",[35,2075,2076,2078,2081],{"class":37,"line":92},[35,2077,1091],{"class":41},[35,2079,2080],{"class":45}," Config",[35,2082,67],{"class":49},[35,2084,2085,2088,2090,2093,2095,2098,2102,2104,2106],{"class":37,"line":441},[35,2086,2087],{"class":49},"    tags: list[",[35,2089,265],{"class":53},[35,2091,2092],{"class":49},"] ",[35,2094,611],{"class":41},[35,2096,2097],{"class":49}," field(",[35,2099,2101],{"class":2100},"sCrzJ","default_factory",[35,2103,611],{"class":41},[35,2105,888],{"class":53},[35,2107,620],{"class":49},[113,2109,2111,2120,2141,2156],{"className":2110},[116],[118,2112,2114,187,2116,2119],{"className":2113},[121],[123,2115],{"disabled":88,"type":125},[32,2117,2118],{},"tags: list[str] = []"," — dataclasses handle mutable defaults safely, unlike plain functions",[118,2121,2123,2125,2126,2129,2130,2133,2134,2137,2138,2140],{"className":2122},[121],[123,2124],{"disabled":88,"type":125}," The shown ",[32,2127,2128],{},"field(default_factory=list)"," form — a bare mutable literal default (",[32,2131,2132],{},"= []",") raises ",[32,2135,2136],{},"ValueError"," at class-definition time, and ",[32,2139,2101],{}," calls the factory fresh for each new instance",[118,2142,2144,187,2146,2149,2150,2152,2153],{"className":2143},[121],[123,2145],{"disabled":88,"type":125},[32,2147,2148],{},"tags: list[str] = None"," and check for ",[32,2151,271],{}," in ",[32,2154,2155],{},"__post_init__",[118,2157,2159,187,2161,2164],{"className":2158},[121],[123,2160],{"disabled":88,"type":125},[32,2162,2163],{},"tags: List[str] = list"," without calling it",[166,2166,2167,2169,2182],{},[169,2168,171],{},[173,2170,2171,740,2173,2175,2176,2178,2179,2181],{},[176,2172,178],{},[32,2174,2128],{},"; a bare ",[32,2177,2132],{}," default raises ",[32,2180,2136],{}," at class-definition time",[173,2183,2184,187,2186,187,2188,2190,2191,105,2193,105,2195,2197,2198,2201,2202,2204,2205,2208],{},[176,2185,186],{},[176,2187,190],{},[32,2189,1193],{}," explicitly detects a mutable literal default (",[32,2192,888],{},[32,2194,929],{},[32,2196,932],{},") for a field and raises ",[32,2199,2200],{},"ValueError: mutable default \u003Cclass 'list'> for field tags is not allowed"," at class-definition time — dataclasses deliberately guard against the classic Python mutable-default-argument footgun rather than silently sharing one list across instances. ",[32,2203,2128],{}," tells it to call ",[32,2206,2207],{},"list()"," fresh per instance instead. Option A is the tempting mistake because dataclasses look like they might \"fix\" the mutable-default problem automatically — they instead refuse to let you make it.",[14,2210,2211,2223,2280,2333],{"language":16},[18,2212,2214,2215,2218,2219,2222],{"id":2213},"q15-what-is-the-idiomatic-reason-to-prefer-optionalstr-none-over-just-writing-str-none-for-a-parameter-default","Q15. What is the idiomatic reason to prefer ",[32,2216,2217],{},"Optional[str] = None"," over just writing ",[32,2220,2221],{},"str = None"," for a parameter default?",[23,2224,2225],{"language":16},[26,2226,2228],{"className":28,"code":2227,"language":16,"meta":30,"style":30},"def greet(name: str = None) -> None: ...\ndef greet2(name: Optional[str] = None) -> None: ...\n",[32,2229,2230,2254],{"__ignoreMap":30},[35,2231,2232,2234,2237,2240,2242,2244,2246,2248,2250,2252],{"class":37,"line":38},[35,2233,42],{"class":41},[35,2235,2236],{"class":45}," greet",[35,2238,2239],{"class":49},"(name: ",[35,2241,265],{"class":53},[35,2243,1147],{"class":41},[35,2245,1144],{"class":53},[35,2247,62],{"class":49},[35,2249,271],{"class":53},[35,2251,274],{"class":49},[35,2253,277],{"class":53},[35,2255,2256,2258,2261,2264,2266,2268,2270,2272,2274,2276,2278],{"class":37,"line":70},[35,2257,42],{"class":41},[35,2259,2260],{"class":45}," greet2",[35,2262,2263],{"class":49},"(name: Optional[",[35,2265,265],{"class":53},[35,2267,2092],{"class":49},[35,2269,611],{"class":41},[35,2271,1144],{"class":53},[35,2273,62],{"class":49},[35,2275,271],{"class":53},[35,2277,274],{"class":49},[35,2279,277],{"class":53},[113,2281,2283,2293,2314,2325],{"className":2282},[116],[118,2284,2286,187,2288,2290,2291],{"className":2285},[121],[123,2287],{"disabled":88,"type":125},[32,2289,2221],{}," is a ",[32,2292,163],{},[118,2294,2296,2298,2299,2301,2302,2304,2305,2307,2308,2310,2311,2313],{"className":2295},[121],[123,2297],{"disabled":88,"type":125}," They behave identically at runtime, but ",[32,2300,2221],{}," is a lie to static checkers — ",[32,2303,271],{}," isn't a ",[32,2306,265],{},", so mypy flags it, while ",[32,2309,2217],{}," accurately documents that ",[32,2312,271],{}," is a valid value",[118,2315,2317,187,2319,2321,2322,2324],{"className":2316},[121],[123,2318],{"disabled":88,"type":125},[32,2320,313],{}," makes the parameter itself optional to omit, while ",[32,2323,2221],{}," does not",[118,2326,2328,187,2330,2332],{"className":2327},[121],[123,2329],{"disabled":88,"type":125},[32,2331,2217],{}," is slower because it wraps the value in a union object at call time",[166,2334,2335,2337,2351],{},[169,2336,171],{},[173,2338,2339,2341,2342,2344,2345,2347,2348,2350],{},[176,2340,178],{}," B — They behave identically at runtime; ",[32,2343,2221],{}," is inaccurate to static checkers, while ",[32,2346,313],{}," documents that ",[32,2349,271],{}," is valid",[173,2352,2353,187,2355,2357,2358,2361,2362,2364,2365,2367,2368,2370,2371,1024,2373,2375,2376,2378,2379,2381],{},[176,2354,186],{},[176,2356,539],{}," Both run identically since CPython ignores hints, but a strict type checker treats ",[32,2359,2360],{},"name: str = None"," as an error because ",[32,2363,271],{}," is not a ",[32,2366,265],{}," and the annotation says nothing about it being optional. ",[32,2369,313],{}," correctly communicates \"this can be a ",[32,2372,265],{},[32,2374,271],{},"\" to both the checker and human readers. Runtime \"optionality\" (whether you can omit the argument) comes purely from the ",[32,2377,379],{}," default, not from ",[32,2380,375],{}," — that's a separate, commonly conflated concept (option C).",[14,2383,2384,2396,2471,2525],{"language":16},[18,2385,2387,2388,2391,2392,2395],{"id":2386},"q16-when-should-you-prefer-typingprotocol-over-an-abstract-base-class-abcabc-for-defining-an-interface","Q16. When should you prefer ",[32,2389,2390],{},"typing.Protocol"," over an abstract base class (",[32,2393,2394],{},"abc.ABC",") for defining an interface?",[23,2397,2398],{"language":16},[26,2399,2401],{"className":28,"code":2400,"language":16,"meta":30,"style":30},"from typing import Protocol\n\nclass SupportsClose(Protocol):\n    def close(self) -> None: ...\n\ndef cleanup(resource: SupportsClose) -> None:\n    resource.close()\n",[32,2402,2403,2414,2418,2432,2448,2452,2466],{"__ignoreMap":30},[35,2404,2405,2407,2409,2411],{"class":37,"line":38},[35,2406,239],{"class":41},[35,2408,242],{"class":49},[35,2410,245],{"class":41},[35,2412,2413],{"class":49}," Protocol\n",[35,2415,2416],{"class":37,"line":70},[35,2417,89],{"emptyLinePlaceholder":88},[35,2419,2420,2422,2425,2427,2430],{"class":37,"line":85},[35,2421,1091],{"class":41},[35,2423,2424],{"class":45}," SupportsClose",[35,2426,1444],{"class":49},[35,2428,2429],{"class":45},"Protocol",[35,2431,1608],{"class":49},[35,2433,2434,2436,2439,2442,2444,2446],{"class":37,"line":92},[35,2435,1101],{"class":41},[35,2437,2438],{"class":45}," close",[35,2440,2441],{"class":49},"(self) -> ",[35,2443,271],{"class":53},[35,2445,274],{"class":49},[35,2447,277],{"class":53},[35,2449,2450],{"class":37,"line":441},[35,2451,89],{"emptyLinePlaceholder":88},[35,2453,2454,2456,2459,2462,2464],{"class":37,"line":446},[35,2455,42],{"class":41},[35,2457,2458],{"class":45}," cleanup",[35,2460,2461],{"class":49},"(resource: SupportsClose) -> ",[35,2463,271],{"class":53},[35,2465,67],{"class":49},[35,2467,2468],{"class":37,"line":465},[35,2469,2470],{"class":49},"    resource.close()\n",[113,2472,2474,2486,2503,2517],{"className":2473},[116],[118,2475,2477,2479,2480,2482,2483],{"className":2476},[121],[123,2478],{"disabled":88,"type":125}," Never — ",[32,2481,2429],{}," is strictly worse and slower than ",[32,2484,2485],{},"ABC",[118,2487,2489,2491,2492,2495,2496,2499,2500,2502],{"className":2488},[121],[123,2490],{"disabled":88,"type":125}," When you want structural (\"duck\") typing — any object with a matching ",[32,2493,2494],{},"close()"," method satisfies the type statically without explicitly inheriting from ",[32,2497,2498],{},"SupportsClose",", unlike ",[32,2501,2485],{}," which requires nominal subclassing",[118,2504,2506,187,2508,2510,2511,2514,2515],{"className":2505},[121],[123,2507],{"disabled":88,"type":125},[32,2509,2429],{}," requires runtime registration via ",[32,2512,2513],{},"register()",", same as ",[32,2516,2485],{},[118,2518,2520,187,2522,2524],{"className":2519},[121],[123,2521],{"disabled":88,"type":125},[32,2523,2429],{}," can only be used for dataclasses",[166,2526,2527,2529,2540],{},[169,2528,171],{},[173,2530,2531,2533,2534,2536,2537,2539],{},[176,2532,178],{}," B — Use ",[32,2535,2429],{}," for structural typing; any object with a matching method satisfies it without explicit inheritance, unlike ",[32,2538,2485],{},"'s nominal typing",[173,2541,2542,187,2544,187,2546,2548,2549,2551,2552,2555,2556,2558,2559,2561,2562,2564],{},[176,2543,186],{},[176,2545,539],{},[32,2547,2429],{}," implements structural subtyping (PEP 544) — a static checker considers any class that happens to implement the right methods\u002Fattributes as compatible, with zero inheritance relationship required. ",[32,2550,2485],{}," requires explicit ",[32,2553,2554],{},"class Foo(SupportsClose):"," nominal subclassing (or ",[32,2557,2513],{},") to be recognized. This makes ",[32,2560,2429],{}," a good fit for third-party classes you can't modify to add a base class, which is a common real-world constraint ",[32,2563,2485],{}," can't satisfy as cleanly.",[14,2566,2567,2575,2619,2657],{"language":16},[18,2568,2570,2571,2574],{"id":2569},"q17-what-does-marking-a-variable-with-final-actually-do","Q17. What does marking a variable with ",[32,2572,2573],{},"Final"," actually do?",[23,2576,2577],{"language":16},[26,2578,2580],{"className":28,"code":2579,"language":16,"meta":30,"style":30},"from typing import Final\n\nMAX_RETRIES: Final = 3\nMAX_RETRIES = 5\n",[32,2581,2582,2593,2597,2610],{"__ignoreMap":30},[35,2583,2584,2586,2588,2590],{"class":37,"line":38},[35,2585,239],{"class":41},[35,2587,242],{"class":49},[35,2589,245],{"class":41},[35,2591,2592],{"class":49}," Final\n",[35,2594,2595],{"class":37,"line":70},[35,2596,89],{"emptyLinePlaceholder":88},[35,2598,2599,2602,2605,2607],{"class":37,"line":85},[35,2600,2601],{"class":53},"MAX_RETRIES",[35,2603,2604],{"class":49},": Final ",[35,2606,611],{"class":41},[35,2608,2609],{"class":53}," 3\n",[35,2611,2612,2614,2616],{"class":37,"line":92},[35,2613,2601],{"class":53},[35,2615,1147],{"class":41},[35,2617,2618],{"class":53}," 5\n",[113,2620,2622,2631,2643,2649],{"className":2621},[116],[118,2623,2625,2627,2628,2630],{"className":2624},[121],[123,2626],{"disabled":88,"type":125}," It raises ",[32,2629,129],{}," immediately on the reassignment line",[118,2632,2634,2636,2637,2639,2640],{"className":2633},[121],[123,2635],{"disabled":88,"type":125}," It is purely a static-analysis hint — mypy will flag the reassignment as an error, but CPython executes the second line without complaint and ",[32,2638,2601],{}," becomes ",[32,2641,2642],{},"5",[118,2644,2646,2648],{"className":2645},[121],[123,2647],{"disabled":88,"type":125}," It makes the name read-only via the C-level immutability flag",[118,2650,2652,187,2654,2656],{"className":2651},[121],[123,2653],{"disabled":88,"type":125},[32,2655,2573],{}," only works inside classes, not at module level",[166,2658,2659,2661,2670],{},[169,2660,171],{},[173,2662,2663,2665,2666,2639,2668],{},[176,2664,178],{}," B — It's purely a static-analysis hint; CPython executes the reassignment fine and ",[32,2667,2601],{},[32,2669,2642],{},[173,2671,2672,187,2674,2676,2677,2679,2680,2682,2683,2686,2687,2690],{},[176,2673,186],{},[176,2675,190],{}," Like all typing constructs, ",[32,2678,2573],{}," carries zero runtime enforcement — it tells a checker \"treat this as a constant; flag any rebinding,\" but the interpreter has no concept of a ",[32,2681,2573],{},"-protected name and simply rebinds it. Developers coming from languages with real ",[32,2684,2685],{},"const","\u002F",[32,2688,2689],{},"final"," keywords (option C) often expect actual immutability enforcement, which Python's typing system deliberately does not provide — runtime constant-protection would need a different mechanism entirely (e.g., a custom descriptor or just convention).",[14,2692,2693,2701,2772,2833],{"language":16},[18,2694,2696,2697,2700],{"id":2695},"q18-what-is-the-purpose-of-classvar-in-this-dataclass","Q18. What is the purpose of ",[32,2698,2699],{},"ClassVar"," in this dataclass?",[23,2702,2703],{"language":16},[26,2704,2706],{"className":28,"code":2705,"language":16,"meta":30,"style":30},"from typing import ClassVar\nfrom dataclasses import dataclass\n\n@dataclass\nclass Counter:\n    count: int = 0\n    total_instances: ClassVar[int] = 0\n",[32,2707,2708,2719,2730,2734,2738,2747,2759],{"__ignoreMap":30},[35,2709,2710,2712,2714,2716],{"class":37,"line":38},[35,2711,239],{"class":41},[35,2713,242],{"class":49},[35,2715,245],{"class":41},[35,2717,2718],{"class":49}," ClassVar\n",[35,2720,2721,2723,2725,2727],{"class":37,"line":70},[35,2722,239],{"class":41},[35,2724,2059],{"class":49},[35,2726,245],{"class":41},[35,2728,2729],{"class":49}," dataclass\n",[35,2731,2732],{"class":37,"line":85},[35,2733,89],{"emptyLinePlaceholder":88},[35,2735,2736],{"class":37,"line":92},[35,2737,2073],{"class":45},[35,2739,2740,2742,2745],{"class":37,"line":441},[35,2741,1091],{"class":41},[35,2743,2744],{"class":45}," Counter",[35,2746,67],{"class":49},[35,2748,2749,2752,2754,2756],{"class":37,"line":446},[35,2750,2751],{"class":49},"    count: ",[35,2753,54],{"class":53},[35,2755,1147],{"class":41},[35,2757,2758],{"class":53}," 0\n",[35,2760,2761,2764,2766,2768,2770],{"class":37,"line":465},[35,2762,2763],{"class":49},"    total_instances: ClassVar[",[35,2765,54],{"class":53},[35,2767,2092],{"class":49},[35,2769,611],{"class":41},[35,2771,2758],{"class":53},[113,2773,2775,2791,2813,2822],{"className":2774},[116],[118,2776,2778,2780,2781,2784,2785,2787,2788],{"className":2777},[121],[123,2779],{"disabled":88,"type":125}," It marks ",[32,2782,2783],{},"total_instances"," as a per-instance field with default ",[32,2786,644],{},", identical to ",[32,2789,2790],{},"count",[118,2792,2794,2796,2797,2799,2800,2802,2803,2806,2807,2809,2810,2812],{"className":2793},[121],[123,2795],{"disabled":88,"type":125}," It tells both static checkers and ",[32,2798,1193],{}," itself to treat ",[32,2801,2783],{}," as a class-level attribute, excluding it from the generated ",[32,2804,2805],{},"__init__"," and instance fields — unlike ",[32,2808,2790],{},", which becomes a normal ",[32,2811,2805],{}," parameter",[118,2814,2816,2818,2819,2821],{"className":2815},[121],[123,2817],{"disabled":88,"type":125}," It makes ",[32,2820,2783],{}," thread-safe automatically",[118,2823,2825,187,2827,2829,2830,2832],{"className":2824},[121],[123,2826],{"disabled":88,"type":125},[32,2828,2699],{}," has no effect on ",[32,2831,1193],{},"'s generated code; it's purely cosmetic",[166,2834,2835,2837,2848],{},[169,2836,171],{},[173,2838,2839,2841,2842,2844,2845,2847],{},[176,2840,178],{}," B — It marks ",[32,2843,2783],{}," as class-level, excluded from the generated ",[32,2846,2805],{}," and instance fields",[173,2849,2850,187,2852,187,2854,2856,2857,2859,2860,105,2862,1013,2864,2866,2867,2870],{},[176,2851,186],{},[176,2853,539],{},[32,2855,1193],{}," specifically inspects annotations for ",[32,2858,2699],{}," and, unlike ordinary fields, excludes those attributes from the auto-generated ",[32,2861,2805],{},[32,2863,554],{},[32,2865,551],{}," — they remain shared class attributes, exactly like a normal class-body assignment without a dataclass. This is one of the few typing constructs that a real library (",[32,2868,2869],{},"dataclasses",") actually inspects at runtime rather than ignoring, which trips people up (option D) since most other hints genuinely are inert to runtime code.",[14,2872,2873,2881,2956,3020],{"language":16},[18,2874,2876,2877,2880],{"id":2875},"q19-what-is-cast-for-and-what-does-it-do-at-runtime","Q19. What is ",[32,2878,2879],{},"cast()"," for, and what does it do at runtime?",[23,2882,2883],{"language":16},[26,2884,2886],{"className":28,"code":2885,"language":16,"meta":30,"style":30},"from typing import cast\n\ndef get_config() -> dict:\n    ...\n\nraw = get_config()\nport = cast(int, raw[\"port\"])\n",[32,2887,2888,2899,2903,2917,2922,2926,2936],{"__ignoreMap":30},[35,2889,2890,2892,2894,2896],{"class":37,"line":38},[35,2891,239],{"class":41},[35,2893,242],{"class":49},[35,2895,245],{"class":41},[35,2897,2898],{"class":49}," cast\n",[35,2900,2901],{"class":37,"line":70},[35,2902,89],{"emptyLinePlaceholder":88},[35,2904,2905,2907,2910,2913,2915],{"class":37,"line":85},[35,2906,42],{"class":41},[35,2908,2909],{"class":45}," get_config",[35,2911,2912],{"class":49},"() -> ",[35,2914,929],{"class":53},[35,2916,67],{"class":49},[35,2918,2919],{"class":37,"line":92},[35,2920,2921],{"class":53},"    ...\n",[35,2923,2924],{"class":37,"line":441},[35,2925,89],{"emptyLinePlaceholder":88},[35,2927,2928,2931,2933],{"class":37,"line":446},[35,2929,2930],{"class":49},"raw ",[35,2932,611],{"class":41},[35,2934,2935],{"class":49}," get_config()\n",[35,2937,2938,2941,2943,2946,2948,2951,2954],{"class":37,"line":465},[35,2939,2940],{"class":49},"port ",[35,2942,611],{"class":41},[35,2944,2945],{"class":49}," cast(",[35,2947,54],{"class":53},[35,2949,2950],{"class":49},", raw[",[35,2952,2953],{"class":101},"\"port\"",[35,2955,678],{"class":49},[113,2957,2959,2978,2992,3009],{"className":2958},[116],[118,2960,2962,187,2964,2967,2968,2970,2971,2973,2974,2977],{"className":2961},[121],[123,2963],{"disabled":88,"type":125},[32,2965,2966],{},"cast(int, x)"," converts ",[32,2969,479],{}," to an ",[32,2972,54],{},", like ",[32,2975,2976],{},"int(x)",", and raises if conversion fails",[118,2979,2981,187,2983,2985,2986,2988,2989,2991],{"className":2980},[121],[123,2982],{"disabled":88,"type":125},[32,2984,2879],{}," is a pure no-op at runtime — it returns ",[32,2987,479],{}," completely unchanged; it exists only to tell the static checker \"trust me, treat this expression as ",[32,2990,54],{},"\" when the checker can't infer it itself",[118,2993,2995,187,2997,2999,3000,3002,3003,3005,3006,3008],{"className":2994},[121],[123,2996],{"disabled":88,"type":125},[32,2998,2879],{}," validates that ",[32,3001,479],{}," is already an ",[32,3004,54],{}," and raises ",[32,3007,129],{}," otherwise",[118,3010,3012,187,3014,3016,3017,3019],{"className":3011},[121],[123,3013],{"disabled":88,"type":125},[32,3015,2879],{}," deep-copies ",[32,3018,479],{}," before returning it",[166,3021,3022,3024,3031],{},[169,3023,171],{},[173,3025,3026,740,3028,3030],{},[176,3027,178],{},[32,3029,2879],{}," is a pure no-op at runtime; it only tells the static checker to treat the expression as the given type",[173,3032,3033,187,3035,187,3037,3040,3041,3044,3045,3047,3048,3051,3052,105,3055,3058,3059,3062,3063,3065,3066,3069],{},[176,3034,186],{},[176,3036,1214],{},[32,3038,3039],{},"typing.cast(TypeHint, value)"," is implemented essentially as ",[32,3042,3043],{},"return value"," — it performs no conversion, no validation, nothing. It exists purely to override a static checker's inferred type when you know something the checker can't (e.g., after runtime validation it doesn't understand). Confusing it with ",[32,3046,2976],{}," (option A) is a common and dangerous mistake: if ",[32,3049,3050],{},"raw[\"port\"]"," is actually the string ",[32,3053,3054],{},"\"8080\"",[32,3056,3057],{},"cast(int, raw[\"port\"])"," happily returns the string unchanged, and a later arithmetic operation on ",[32,3060,3061],{},"port"," will fail with a ",[32,3064,129],{}," far from where the real bug is — the correct fix is to actually convert\u002Fvalidate the value, e.g. ",[32,3067,3068],{},"port = int(raw[\"port\"])",", not just cast it.",[14,3071,3072,3086,3254,3308],{"language":16},[18,3073,3075,3076,3078,3079,3082,3083,3085],{"id":3074},"q20-what-does-declaring-a-typevar-as-covariant-typevart_co-covarianttrue-enable-that-an-invariant-typevar-does-not","Q20. What does declaring a ",[32,3077,695],{}," as covariant (",[32,3080,3081],{},"TypeVar(\"T_co\", covariant=True)",") enable that an invariant ",[32,3084,695],{}," does not?",[23,3087,3088],{"language":16},[26,3089,3091],{"className":28,"code":3090,"language":16,"meta":30,"style":30},"from typing import TypeVar, Generic\n\nT_co = TypeVar(\"T_co\", covariant=True)\n\nclass ReadOnlyBox(Generic[T_co]):\n    def __init__(self, item: T_co) -> None:\n        self._item = item\n    def get(self) -> T_co:\n        return self._item\n\ndef print_box(box: \"ReadOnlyBox[object]\") -> None:\n    print(box.get())\n\nint_box: ReadOnlyBox[int] = ReadOnlyBox(42)\nprint_box(int_box)\n",[32,3092,3093,3104,3108,3131,3135,3145,3158,3170,3180,3191,3195,3215,3224,3229,3248],{"__ignoreMap":30},[35,3094,3095,3097,3099,3101],{"class":37,"line":38},[35,3096,239],{"class":41},[35,3098,242],{"class":49},[35,3100,245],{"class":41},[35,3102,3103],{"class":49}," TypeVar, Generic\n",[35,3105,3106],{"class":37,"line":70},[35,3107,89],{"emptyLinePlaceholder":88},[35,3109,3110,3113,3115,3117,3120,3122,3125,3127,3129],{"class":37,"line":85},[35,3111,3112],{"class":49},"T_co ",[35,3114,611],{"class":41},[35,3116,614],{"class":49},[35,3118,3119],{"class":101},"\"T_co\"",[35,3121,105],{"class":49},[35,3123,3124],{"class":2100},"covariant",[35,3126,611],{"class":41},[35,3128,1323],{"class":53},[35,3130,620],{"class":49},[35,3132,3133],{"class":37,"line":92},[35,3134,89],{"emptyLinePlaceholder":88},[35,3136,3137,3139,3142],{"class":37,"line":441},[35,3138,1091],{"class":41},[35,3140,3141],{"class":45}," ReadOnlyBox",[35,3143,3144],{"class":49},"(Generic[T_co]):\n",[35,3146,3147,3149,3151,3154,3156],{"class":37,"line":446},[35,3148,1101],{"class":41},[35,3150,1104],{"class":53},[35,3152,3153],{"class":49},"(self, item: T_co) -> ",[35,3155,271],{"class":53},[35,3157,67],{"class":49},[35,3159,3160,3162,3165,3167],{"class":37,"line":465},[35,3161,1120],{"class":53},[35,3163,3164],{"class":49},"._item ",[35,3166,611],{"class":41},[35,3168,3169],{"class":49}," item\n",[35,3171,3172,3174,3177],{"class":37,"line":654},[35,3173,1101],{"class":41},[35,3175,3176],{"class":45}," get",[35,3178,3179],{"class":49},"(self) -> T_co:\n",[35,3181,3182,3185,3188],{"class":37,"line":1864},[35,3183,3184],{"class":41},"        return",[35,3186,3187],{"class":53}," self",[35,3189,3190],{"class":49},"._item\n",[35,3192,3193],{"class":37,"line":1869},[35,3194,89],{"emptyLinePlaceholder":88},[35,3196,3198,3200,3203,3206,3209,3211,3213],{"class":37,"line":3197},11,[35,3199,42],{"class":41},[35,3201,3202],{"class":45}," print_box",[35,3204,3205],{"class":49},"(box: ",[35,3207,3208],{"class":101},"\"ReadOnlyBox[object]\"",[35,3210,62],{"class":49},[35,3212,271],{"class":53},[35,3214,67],{"class":49},[35,3216,3218,3221],{"class":37,"line":3217},12,[35,3219,3220],{"class":53},"    print",[35,3222,3223],{"class":49},"(box.get())\n",[35,3225,3227],{"class":37,"line":3226},13,[35,3228,89],{"emptyLinePlaceholder":88},[35,3230,3232,3235,3237,3239,3241,3244,3246],{"class":37,"line":3231},14,[35,3233,3234],{"class":49},"int_box: ReadOnlyBox[",[35,3236,54],{"class":53},[35,3238,2092],{"class":49},[35,3240,611],{"class":41},[35,3242,3243],{"class":49}," ReadOnlyBox(",[35,3245,1435],{"class":53},[35,3247,620],{"class":49},[35,3249,3251],{"class":37,"line":3250},15,[35,3252,3253],{"class":49},"print_box(int_box)\n",[113,3255,3257,3263,3290,3300],{"className":3256},[116],[118,3258,3260,3262],{"className":3259},[121],[123,3261],{"disabled":88,"type":125}," Covariance has a runtime performance cost proportional to the number of subtypes",[118,3264,3266,3268,3269,3272,3273,3276,3277,3280,3281,3283,3284,3286,3287,3289],{"className":3265},[121],[123,3267],{"disabled":88,"type":125}," With a covariant ",[32,3270,3271],{},"T_co",", a static checker allows ",[32,3274,3275],{},"ReadOnlyBox[int]"," to be used wherever ",[32,3278,3279],{},"ReadOnlyBox[object]"," is expected (since ",[32,3282,54],{}," is a subtype of ",[32,3285,396],{},"), matching the intuitive subtyping of read-only containers; an invariant ",[32,3288,695],{}," would reject this assignment as a type error even though it works fine at runtime",[118,3291,3293,3295,3296,3299],{"className":3292},[121],[123,3294],{"disabled":88,"type":125}," Covariance lets you mutate ",[32,3297,3298],{},"_item"," to any type at runtime without restriction",[118,3301,3303,3305,3306],{"className":3302},[121],[123,3304],{"disabled":88,"type":125}," Covariant TypeVars are required for any class that uses ",[32,3307,2805],{},[166,3309,3310,3312,3323],{},[169,3311,171],{},[173,3313,3314,3316,3317,3319,3320,3322],{},[176,3315,178],{}," B — Covariance lets a static checker treat ",[32,3318,3275],{}," as compatible with ",[32,3321,3279],{},", matching real subtyping for read-only containers; invariant would reject it",[173,3324,3325,187,3327,3329,3330,3332,3333,198,3335,3337,3338,3340,3341,3343,3344,3346,3347,3351,3352,3355,3356,3358,3359,2290,3362,3365,3366,3368,3369,3372,3373,3375],{},[176,3326,186],{},[176,3328,539],{}," By default, ",[32,3331,695],{},"s are invariant — a checker treats ",[32,3334,3275],{},[32,3336,3279],{}," as unrelated types even though ",[32,3339,54],{}," is an ",[32,3342,396],{},". Marking ",[32,3345,3271],{}," covariant tells the checker it's safe to treat the relationship like the underlying type relationship ",[3348,3349,3350],"em",{},"because"," the box is read-only (",[32,3353,3354],{},"get()"," only, never accepts a new ",[32,3357,3271],{}," from outside after construction) — this mirrors why ",[32,3360,3361],{},"Sequence[int]",[32,3363,3364],{},"Sequence[object]"," in typeshed but ",[32,3367,723],{}," is intentionally not treated as a ",[32,3370,3371],{},"list[object]"," (since ",[32,3374,888],{}," is mutable and allows appending, which invariance protects against). None of this variance machinery has any runtime effect at all — it exists solely to make static checking of generic containers match real-world expectations.",[3377,3378,3379],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":30,"searchDepth":70,"depth":70,"links":3381},[3382,3383,3384,3386,3387,3389,3390,3392,3394,3396,3398,3400,3402,3404,3406,3408,3410,3412,3414,3416],{"id":20,"depth":85,"text":21},{"id":226,"depth":85,"text":227},{"id":388,"depth":85,"text":3385},"Q3. What is the key practical difference between annotating a parameter as Any versus object?",{"id":580,"depth":85,"text":581},{"id":785,"depth":85,"text":3388},"Q5. Which statement correctly describes how mypy relates to running your program?",{"id":861,"depth":85,"text":862},{"id":966,"depth":85,"text":3391},"Q7. What does Callable[[int, str], bool] describe?",{"id":1071,"depth":85,"text":3393},"Q8. A class needs to reference its own type in a method signature before the class body finishes executing. Which approach avoids a NameError at class-definition time on Python versions before 3.10 without from __future__ import annotations?",{"id":1227,"depth":85,"text":3395},"Q9. Why would a codebase use if TYPE_CHECKING: around an import?",{"id":1376,"depth":85,"text":3397},"Q10. What does UserId = NewType(\"UserId\", int) actually produce at runtime?",{"id":1567,"depth":85,"text":3399},"Q11. What runtime guarantee does a TypedDict provide?",{"id":1773,"depth":85,"text":3401},"Q12. What happens if you call a function decorated with multiple @overload signatures using arguments that don't match any of the declared overloads, when running the actual script (not a type checker)?",{"id":1966,"depth":85,"text":3403},"Q13. A function is annotated def get_ids() -> list[int]: but its body is return []. Is this a problem at runtime?",{"id":2040,"depth":85,"text":3405},"Q14. What is the correct, type-hint-safe way to give a dataclass field a mutable default value?",{"id":2213,"depth":85,"text":3407},"Q15. What is the idiomatic reason to prefer Optional[str] = None over just writing str = None for a parameter default?",{"id":2386,"depth":85,"text":3409},"Q16. When should you prefer typing.Protocol over an abstract base class (abc.ABC) for defining an interface?",{"id":2569,"depth":85,"text":3411},"Q17. What does marking a variable with Final actually do?",{"id":2695,"depth":85,"text":3413},"Q18. What is the purpose of ClassVar in this dataclass?",{"id":2875,"depth":85,"text":3415},"Q19. What is cast() for, and what does it do at runtime?",{"id":3074,"depth":85,"text":3417},"Q20. What does declaring a TypeVar as covariant (TypeVar(\"T_co\", covariant=True)) enable that an invariant TypeVar does not?","md",{},"\u002Fpython\u002F20-type-hints-and-typing",{"title":5,"description":30},"python\u002F20-type-hints-and-typing","HBIbjotG8WeO_IhzXo7xFEweB5xLy3y-mxqmaw5GP_4",1787335398286]