[{"data":1,"prerenderedAt":3360},["ShallowReactive",2],{"page-\u002Fpython\u002F10-functional-programming":3},{"id":4,"title":5,"body":6,"description":36,"extension":3354,"meta":3355,"navigation":59,"path":3356,"seo":3357,"stem":3358,"__hash__":3359},"content\u002Fpython\u002F10-functional-programming.md","10 — Functional Programming",{"type":7,"value":8,"toc":3331},"minimark",[9,13,27,784,3327],[10,11,5],"h1",{"id":12},"_10-functional-programming",[14,15,17,18,22,23,26],"h2",{"id":16},"production-pipeline-functoolspartial-operator-composition","Production Pipeline — ",[19,20,21],"code",{},"functools.partial"," + ",[19,24,25],{},"operator"," + Composition",[28,29,31],"code-wrapper",{"language":30},"python",[32,33,37],"pre",{"className":34,"code":35,"language":30,"meta":36,"style":36},"language-python shiki shiki-themes github-light github-dark","# ── A data transformation pipeline using functional composition ──\n# Each stage is a curried\u002Fpartial function — no lambdas, all named, all testable.\n\nimport functools\nimport operator\nfrom dataclasses import dataclass\n\n@dataclass\nclass Order:\n    id: int\n    items: list[dict]    # each: {\"name\": str, \"price\": float, \"qty\": int}\n    status: str\n\n# ── A complete pipeline: filter → map → reduce, all lazy, all composable ──\ndef process_orders(orders: list[Order], target_status: str) -> dict:\n    \"\"\"Filter by status, compute per-order totals, return summary statistics.\"\"\"\n    import statistics\n\n    # Stage 1: filter — generator expression, lazy\n    matching = (o for o in orders if o.status == target_status)\n\n    # Stage 2: map — compute order total via sum of (price * qty) per item\n    totals = (\n        sum(item[\"price\"] * item[\"qty\"] for item in o.items)\n        for o in matching\n    )\n\n    # Stage 3: reduce — materialize for multi-pass (mean AND max AND count)\n    totals_list = list(totals)   # must materialize — we need 3 passes (mean, max, len)\n\n    return {\n        \"count\": len(totals_list),\n        \"mean\": statistics.mean(totals_list) if totals_list else 0,\n        \"max\": max(totals_list) if totals_list else 0,\n        \"total\": sum(totals_list),\n    }\n\norders = [\n    Order(1, [{\"name\": \"book\", \"price\": 20.0, \"qty\": 2}], \"shipped\"),\n    Order(2, [{\"name\": \"pen\", \"price\": 5.0, \"qty\": 10}], \"pending\"),\n    Order(3, [{\"name\": \"laptop\", \"price\": 999.0, \"qty\": 1}], \"shipped\"),\n]\nprint(process_orders(orders, \"shipped\"))\n# {'count': 2, 'mean': 519.5, 'max': 999.0, 'total': 1039.0}\n\n# ── operator module: replace common lambdas with named, C-implemented functions ──\npeople = [{\"name\": \"Ada\", \"age\": 36}, {\"name\": \"Grace\", \"age\": 85}]\n\n# ANTI-PATTERN: lambdas for trivial property access — slower, no name in tracebacks\nby_age_lambda = sorted(people, key=lambda p: p[\"age\"])\n\n# CORRECT: operator.itemgetter — C-implemented, named, faster\nby_age = sorted(people, key=operator.itemgetter(\"age\"))\nby_name = sorted(people, key=operator.itemgetter(\"name\"))\n# itemgetter(\"age\") returns a callable equivalent to lambda p: p[\"age\"] but faster\n","",[19,38,39,48,54,61,72,80,94,99,106,118,131,146,155,160,166,188,195,204,209,215,251,256,262,273,309,322,328,333,339,356,361,370,384,407,431,444,450,455,466,514,555,595,601,615,621,626,632,682,687,693,722,727,733,756,778],{"__ignoreMap":36},[40,41,44],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sdCPZ","# ── A data transformation pipeline using functional composition ──\n",[40,49,51],{"class":42,"line":50},2,[40,52,53],{"class":46},"# Each stage is a curried\u002Fpartial function — no lambdas, all named, all testable.\n",[40,55,57],{"class":42,"line":56},3,[40,58,60],{"emptyLinePlaceholder":59},true,"\n",[40,62,64,68],{"class":42,"line":63},4,[40,65,67],{"class":66},"svdQ7","import",[40,69,71],{"class":70},"ssxIu"," functools\n",[40,73,75,77],{"class":42,"line":74},5,[40,76,67],{"class":66},[40,78,79],{"class":70}," operator\n",[40,81,83,86,89,91],{"class":42,"line":82},6,[40,84,85],{"class":66},"from",[40,87,88],{"class":70}," dataclasses ",[40,90,67],{"class":66},[40,92,93],{"class":70}," dataclass\n",[40,95,97],{"class":42,"line":96},7,[40,98,60],{"emptyLinePlaceholder":59},[40,100,102],{"class":42,"line":101},8,[40,103,105],{"class":104},"sIsaT","@dataclass\n",[40,107,109,112,115],{"class":42,"line":108},9,[40,110,111],{"class":66},"class",[40,113,114],{"class":104}," Order",[40,116,117],{"class":70},":\n",[40,119,121,125,128],{"class":42,"line":120},10,[40,122,124],{"class":123},"snvgF","    id",[40,126,127],{"class":70},": ",[40,129,130],{"class":123},"int\n",[40,132,134,137,140,143],{"class":42,"line":133},11,[40,135,136],{"class":70},"    items: list[",[40,138,139],{"class":123},"dict",[40,141,142],{"class":70},"]    ",[40,144,145],{"class":46},"# each: {\"name\": str, \"price\": float, \"qty\": int}\n",[40,147,149,152],{"class":42,"line":148},12,[40,150,151],{"class":70},"    status: ",[40,153,154],{"class":123},"str\n",[40,156,158],{"class":42,"line":157},13,[40,159,60],{"emptyLinePlaceholder":59},[40,161,163],{"class":42,"line":162},14,[40,164,165],{"class":46},"# ── A complete pipeline: filter → map → reduce, all lazy, all composable ──\n",[40,167,169,172,175,178,181,184,186],{"class":42,"line":168},15,[40,170,171],{"class":66},"def",[40,173,174],{"class":104}," process_orders",[40,176,177],{"class":70},"(orders: list[Order], target_status: ",[40,179,180],{"class":123},"str",[40,182,183],{"class":70},") -> ",[40,185,139],{"class":123},[40,187,117],{"class":70},[40,189,191],{"class":42,"line":190},16,[40,192,194],{"class":193},"sJ6F3","    \"\"\"Filter by status, compute per-order totals, return summary statistics.\"\"\"\n",[40,196,198,201],{"class":42,"line":197},17,[40,199,200],{"class":66},"    import",[40,202,203],{"class":70}," statistics\n",[40,205,207],{"class":42,"line":206},18,[40,208,60],{"emptyLinePlaceholder":59},[40,210,212],{"class":42,"line":211},19,[40,213,214],{"class":46},"    # Stage 1: filter — generator expression, lazy\n",[40,216,218,221,224,227,230,233,236,239,242,245,248],{"class":42,"line":217},20,[40,219,220],{"class":70},"    matching ",[40,222,223],{"class":66},"=",[40,225,226],{"class":70}," (o ",[40,228,229],{"class":66},"for",[40,231,232],{"class":70}," o ",[40,234,235],{"class":66},"in",[40,237,238],{"class":70}," orders ",[40,240,241],{"class":66},"if",[40,243,244],{"class":70}," o.status ",[40,246,247],{"class":66},"==",[40,249,250],{"class":70}," target_status)\n",[40,252,254],{"class":42,"line":253},21,[40,255,60],{"emptyLinePlaceholder":59},[40,257,259],{"class":42,"line":258},22,[40,260,261],{"class":46},"    # Stage 2: map — compute order total via sum of (price * qty) per item\n",[40,263,265,268,270],{"class":42,"line":264},23,[40,266,267],{"class":70},"    totals ",[40,269,223],{"class":66},[40,271,272],{"class":70}," (\n",[40,274,276,279,282,285,288,291,294,297,299,301,304,306],{"class":42,"line":275},24,[40,277,278],{"class":123},"        sum",[40,280,281],{"class":70},"(item[",[40,283,284],{"class":193},"\"price\"",[40,286,287],{"class":70},"] ",[40,289,290],{"class":66},"*",[40,292,293],{"class":70}," item[",[40,295,296],{"class":193},"\"qty\"",[40,298,287],{"class":70},[40,300,229],{"class":66},[40,302,303],{"class":70}," item ",[40,305,235],{"class":66},[40,307,308],{"class":70}," o.items)\n",[40,310,312,315,317,319],{"class":42,"line":311},25,[40,313,314],{"class":66},"        for",[40,316,232],{"class":70},[40,318,235],{"class":66},[40,320,321],{"class":70}," matching\n",[40,323,325],{"class":42,"line":324},26,[40,326,327],{"class":70},"    )\n",[40,329,331],{"class":42,"line":330},27,[40,332,60],{"emptyLinePlaceholder":59},[40,334,336],{"class":42,"line":335},28,[40,337,338],{"class":46},"    # Stage 3: reduce — materialize for multi-pass (mean AND max AND count)\n",[40,340,342,345,347,350,353],{"class":42,"line":341},29,[40,343,344],{"class":70},"    totals_list ",[40,346,223],{"class":66},[40,348,349],{"class":123}," list",[40,351,352],{"class":70},"(totals)   ",[40,354,355],{"class":46},"# must materialize — we need 3 passes (mean, max, len)\n",[40,357,359],{"class":42,"line":358},30,[40,360,60],{"emptyLinePlaceholder":59},[40,362,364,367],{"class":42,"line":363},31,[40,365,366],{"class":66},"    return",[40,368,369],{"class":70}," {\n",[40,371,373,376,378,381],{"class":42,"line":372},32,[40,374,375],{"class":193},"        \"count\"",[40,377,127],{"class":70},[40,379,380],{"class":123},"len",[40,382,383],{"class":70},"(totals_list),\n",[40,385,387,390,393,395,398,401,404],{"class":42,"line":386},33,[40,388,389],{"class":193},"        \"mean\"",[40,391,392],{"class":70},": statistics.mean(totals_list) ",[40,394,241],{"class":66},[40,396,397],{"class":70}," totals_list ",[40,399,400],{"class":66},"else",[40,402,403],{"class":123}," 0",[40,405,406],{"class":70},",\n",[40,408,410,413,415,418,421,423,425,427,429],{"class":42,"line":409},34,[40,411,412],{"class":193},"        \"max\"",[40,414,127],{"class":70},[40,416,417],{"class":123},"max",[40,419,420],{"class":70},"(totals_list) ",[40,422,241],{"class":66},[40,424,397],{"class":70},[40,426,400],{"class":66},[40,428,403],{"class":123},[40,430,406],{"class":70},[40,432,434,437,439,442],{"class":42,"line":433},35,[40,435,436],{"class":193},"        \"total\"",[40,438,127],{"class":70},[40,440,441],{"class":123},"sum",[40,443,383],{"class":70},[40,445,447],{"class":42,"line":446},36,[40,448,449],{"class":70},"    }\n",[40,451,453],{"class":42,"line":452},37,[40,454,60],{"emptyLinePlaceholder":59},[40,456,458,461,463],{"class":42,"line":457},38,[40,459,460],{"class":70},"orders ",[40,462,223],{"class":66},[40,464,465],{"class":70}," [\n",[40,467,469,472,475,478,481,483,486,489,491,493,496,498,500,502,505,508,511],{"class":42,"line":468},39,[40,470,471],{"class":70},"    Order(",[40,473,474],{"class":123},"1",[40,476,477],{"class":70},", [{",[40,479,480],{"class":193},"\"name\"",[40,482,127],{"class":70},[40,484,485],{"class":193},"\"book\"",[40,487,488],{"class":70},", ",[40,490,284],{"class":193},[40,492,127],{"class":70},[40,494,495],{"class":123},"20.0",[40,497,488],{"class":70},[40,499,296],{"class":193},[40,501,127],{"class":70},[40,503,504],{"class":123},"2",[40,506,507],{"class":70},"}], ",[40,509,510],{"class":193},"\"shipped\"",[40,512,513],{"class":70},"),\n",[40,515,517,519,521,523,525,527,530,532,534,536,539,541,543,545,548,550,553],{"class":42,"line":516},40,[40,518,471],{"class":70},[40,520,504],{"class":123},[40,522,477],{"class":70},[40,524,480],{"class":193},[40,526,127],{"class":70},[40,528,529],{"class":193},"\"pen\"",[40,531,488],{"class":70},[40,533,284],{"class":193},[40,535,127],{"class":70},[40,537,538],{"class":123},"5.0",[40,540,488],{"class":70},[40,542,296],{"class":193},[40,544,127],{"class":70},[40,546,547],{"class":123},"10",[40,549,507],{"class":70},[40,551,552],{"class":193},"\"pending\"",[40,554,513],{"class":70},[40,556,558,560,563,565,567,569,572,574,576,578,581,583,585,587,589,591,593],{"class":42,"line":557},41,[40,559,471],{"class":70},[40,561,562],{"class":123},"3",[40,564,477],{"class":70},[40,566,480],{"class":193},[40,568,127],{"class":70},[40,570,571],{"class":193},"\"laptop\"",[40,573,488],{"class":70},[40,575,284],{"class":193},[40,577,127],{"class":70},[40,579,580],{"class":123},"999.0",[40,582,488],{"class":70},[40,584,296],{"class":193},[40,586,127],{"class":70},[40,588,474],{"class":123},[40,590,507],{"class":70},[40,592,510],{"class":193},[40,594,513],{"class":70},[40,596,598],{"class":42,"line":597},42,[40,599,600],{"class":70},"]\n",[40,602,604,607,610,612],{"class":42,"line":603},43,[40,605,606],{"class":123},"print",[40,608,609],{"class":70},"(process_orders(orders, ",[40,611,510],{"class":193},[40,613,614],{"class":70},"))\n",[40,616,618],{"class":42,"line":617},44,[40,619,620],{"class":46},"# {'count': 2, 'mean': 519.5, 'max': 999.0, 'total': 1039.0}\n",[40,622,624],{"class":42,"line":623},45,[40,625,60],{"emptyLinePlaceholder":59},[40,627,629],{"class":42,"line":628},46,[40,630,631],{"class":46},"# ── operator module: replace common lambdas with named, C-implemented functions ──\n",[40,633,635,638,640,643,645,647,650,652,655,657,660,663,665,667,670,672,674,676,679],{"class":42,"line":634},47,[40,636,637],{"class":70},"people ",[40,639,223],{"class":66},[40,641,642],{"class":70}," [{",[40,644,480],{"class":193},[40,646,127],{"class":70},[40,648,649],{"class":193},"\"Ada\"",[40,651,488],{"class":70},[40,653,654],{"class":193},"\"age\"",[40,656,127],{"class":70},[40,658,659],{"class":123},"36",[40,661,662],{"class":70},"}, {",[40,664,480],{"class":193},[40,666,127],{"class":70},[40,668,669],{"class":193},"\"Grace\"",[40,671,488],{"class":70},[40,673,654],{"class":193},[40,675,127],{"class":70},[40,677,678],{"class":123},"85",[40,680,681],{"class":70},"}]\n",[40,683,685],{"class":42,"line":684},48,[40,686,60],{"emptyLinePlaceholder":59},[40,688,690],{"class":42,"line":689},49,[40,691,692],{"class":46},"# ANTI-PATTERN: lambdas for trivial property access — slower, no name in tracebacks\n",[40,694,696,699,701,704,707,711,714,717,719],{"class":42,"line":695},50,[40,697,698],{"class":70},"by_age_lambda ",[40,700,223],{"class":66},[40,702,703],{"class":123}," sorted",[40,705,706],{"class":70},"(people, ",[40,708,710],{"class":709},"sCrzJ","key",[40,712,713],{"class":66},"=lambda",[40,715,716],{"class":70}," p: p[",[40,718,654],{"class":193},[40,720,721],{"class":70},"])\n",[40,723,725],{"class":42,"line":724},51,[40,726,60],{"emptyLinePlaceholder":59},[40,728,730],{"class":42,"line":729},52,[40,731,732],{"class":46},"# CORRECT: operator.itemgetter — C-implemented, named, faster\n",[40,734,736,739,741,743,745,747,749,752,754],{"class":42,"line":735},53,[40,737,738],{"class":70},"by_age ",[40,740,223],{"class":66},[40,742,703],{"class":123},[40,744,706],{"class":70},[40,746,710],{"class":709},[40,748,223],{"class":66},[40,750,751],{"class":70},"operator.itemgetter(",[40,753,654],{"class":193},[40,755,614],{"class":70},[40,757,759,762,764,766,768,770,772,774,776],{"class":42,"line":758},54,[40,760,761],{"class":70},"by_name ",[40,763,223],{"class":66},[40,765,703],{"class":123},[40,767,706],{"class":70},[40,769,710],{"class":709},[40,771,223],{"class":66},[40,773,751],{"class":70},[40,775,480],{"class":193},[40,777,614],{"class":70},[40,779,781],{"class":42,"line":780},55,[40,782,783],{"class":46},"# itemgetter(\"age\") returns a callable equivalent to lambda p: p[\"age\"] but faster\n",[28,785,786,1201,1267,1277,1486,1506,1605,1612,1824,1863,1869,2043,2062,2069,2250,2270,2278,2349,2353,2435,2455,2462,2663,2673,2677,2814,2828,2832,2927,2931,3039,3043,3046,3194,3272,3276],{"language":30},[32,787,789],{"className":34,"code":788,"language":30,"meta":36,"style":36},"# ── Production: lru_cache memory leak on instance methods — the silent killer ──\n# ANTI-PATTERN: @lru_cache on an instance method keeps every instance alive forever\n\nfrom functools import lru_cache\nimport gc\n\nclass ExpensiveService:\n    def __init__(self, dataset_id: str):\n        self.dataset_id = dataset_id\n\n    @lru_cache(maxsize=None)   # BUG: caches on (self, *args) — self is part of the key!\n    def compute(self, key: str) -> float:\n        \"\"\"Expensive computation — but the cache holds a STRONG reference to self.\"\"\"\n        return hash(f\"{self.dataset_id}:{key}\") % 1000\n\n# Demonstrate the leak: create and drop instances, observe they're NOT collected\nservice = ExpensiveService(\"ds-1\")\nservice.compute(\"x\")           # caches result — cache now holds a ref to `service`\ndel service                    # drop our reference\ngc.collect()\n# The service object is STILL alive — lru_cache's internal dict holds (service, \"x\") -> result\n# In a long-running web process creating millions of service objects, this is a memory leak.\n\n# CORRECT: cache a module-level function taking only hashable args, not `self`\n@lru_cache(maxsize=1024)\ndef _compute_cached(dataset_id: str, key: str) -> float:\n    \"\"\"Module-level cache — no instance reference, bounded by maxsize.\"\"\"\n    return hash(f\"{dataset_id}:{key}\") % 1000\n\nclass ExpensiveServiceFixed:\n    def __init__(self, dataset_id: str):\n        self.dataset_id = dataset_id\n\n    def compute(self, key: str) -> float:\n        return _compute_cached(self.dataset_id, key)   # delegate to module-level cache\n\n# Now instances are freely collectable — the cache only holds (str, str) -> float\n",[19,790,791,796,801,805,817,824,828,837,853,866,870,898,917,922,968,972,977,993,1007,1018,1023,1028,1033,1037,1042,1058,1081,1086,1121,1125,1134,1146,1156,1160,1176,1192,1196],{"__ignoreMap":36},[40,792,793],{"class":42,"line":43},[40,794,795],{"class":46},"# ── Production: lru_cache memory leak on instance methods — the silent killer ──\n",[40,797,798],{"class":42,"line":50},[40,799,800],{"class":46},"# ANTI-PATTERN: @lru_cache on an instance method keeps every instance alive forever\n",[40,802,803],{"class":42,"line":56},[40,804,60],{"emptyLinePlaceholder":59},[40,806,807,809,812,814],{"class":42,"line":63},[40,808,85],{"class":66},[40,810,811],{"class":70}," functools ",[40,813,67],{"class":66},[40,815,816],{"class":70}," lru_cache\n",[40,818,819,821],{"class":42,"line":74},[40,820,67],{"class":66},[40,822,823],{"class":70}," gc\n",[40,825,826],{"class":42,"line":82},[40,827,60],{"emptyLinePlaceholder":59},[40,829,830,832,835],{"class":42,"line":96},[40,831,111],{"class":66},[40,833,834],{"class":104}," ExpensiveService",[40,836,117],{"class":70},[40,838,839,842,845,848,850],{"class":42,"line":101},[40,840,841],{"class":66},"    def",[40,843,844],{"class":123}," __init__",[40,846,847],{"class":70},"(self, dataset_id: ",[40,849,180],{"class":123},[40,851,852],{"class":70},"):\n",[40,854,855,858,861,863],{"class":42,"line":108},[40,856,857],{"class":123},"        self",[40,859,860],{"class":70},".dataset_id ",[40,862,223],{"class":66},[40,864,865],{"class":70}," dataset_id\n",[40,867,868],{"class":42,"line":120},[40,869,60],{"emptyLinePlaceholder":59},[40,871,872,875,878,881,883,886,889,892,895],{"class":42,"line":133},[40,873,874],{"class":104},"    @lru_cache",[40,876,877],{"class":70},"(",[40,879,880],{"class":709},"maxsize",[40,882,223],{"class":66},[40,884,885],{"class":123},"None",[40,887,888],{"class":70},")   ",[40,890,891],{"class":46},"# ",[40,893,894],{"class":66},"BUG",[40,896,897],{"class":46},": caches on (self, *args) — self is part of the key!\n",[40,899,900,902,905,908,910,912,915],{"class":42,"line":148},[40,901,841],{"class":66},[40,903,904],{"class":104}," compute",[40,906,907],{"class":70},"(self, key: ",[40,909,180],{"class":123},[40,911,183],{"class":70},[40,913,914],{"class":123},"float",[40,916,117],{"class":70},[40,918,919],{"class":42,"line":157},[40,920,921],{"class":193},"        \"\"\"Expensive computation — but the cache holds a STRONG reference to self.\"\"\"\n",[40,923,924,927,930,932,935,938,941,944,947,950,953,955,957,959,962,965],{"class":42,"line":162},[40,925,926],{"class":66},"        return",[40,928,929],{"class":123}," hash",[40,931,877],{"class":70},[40,933,934],{"class":66},"f",[40,936,937],{"class":193},"\"",[40,939,940],{"class":123},"{self",[40,942,943],{"class":70},".dataset_id",[40,945,946],{"class":123},"}",[40,948,949],{"class":193},":",[40,951,952],{"class":123},"{",[40,954,710],{"class":70},[40,956,946],{"class":123},[40,958,937],{"class":193},[40,960,961],{"class":70},") ",[40,963,964],{"class":66},"%",[40,966,967],{"class":123}," 1000\n",[40,969,970],{"class":42,"line":168},[40,971,60],{"emptyLinePlaceholder":59},[40,973,974],{"class":42,"line":190},[40,975,976],{"class":46},"# Demonstrate the leak: create and drop instances, observe they're NOT collected\n",[40,978,979,982,984,987,990],{"class":42,"line":197},[40,980,981],{"class":70},"service ",[40,983,223],{"class":66},[40,985,986],{"class":70}," ExpensiveService(",[40,988,989],{"class":193},"\"ds-1\"",[40,991,992],{"class":70},")\n",[40,994,995,998,1001,1004],{"class":42,"line":206},[40,996,997],{"class":70},"service.compute(",[40,999,1000],{"class":193},"\"x\"",[40,1002,1003],{"class":70},")           ",[40,1005,1006],{"class":46},"# caches result — cache now holds a ref to `service`\n",[40,1008,1009,1012,1015],{"class":42,"line":211},[40,1010,1011],{"class":66},"del",[40,1013,1014],{"class":70}," service                    ",[40,1016,1017],{"class":46},"# drop our reference\n",[40,1019,1020],{"class":42,"line":217},[40,1021,1022],{"class":70},"gc.collect()\n",[40,1024,1025],{"class":42,"line":253},[40,1026,1027],{"class":46},"# The service object is STILL alive — lru_cache's internal dict holds (service, \"x\") -> result\n",[40,1029,1030],{"class":42,"line":258},[40,1031,1032],{"class":46},"# In a long-running web process creating millions of service objects, this is a memory leak.\n",[40,1034,1035],{"class":42,"line":264},[40,1036,60],{"emptyLinePlaceholder":59},[40,1038,1039],{"class":42,"line":275},[40,1040,1041],{"class":46},"# CORRECT: cache a module-level function taking only hashable args, not `self`\n",[40,1043,1044,1047,1049,1051,1053,1056],{"class":42,"line":311},[40,1045,1046],{"class":104},"@lru_cache",[40,1048,877],{"class":70},[40,1050,880],{"class":709},[40,1052,223],{"class":66},[40,1054,1055],{"class":123},"1024",[40,1057,992],{"class":70},[40,1059,1060,1062,1065,1068,1070,1073,1075,1077,1079],{"class":42,"line":324},[40,1061,171],{"class":66},[40,1063,1064],{"class":104}," _compute_cached",[40,1066,1067],{"class":70},"(dataset_id: ",[40,1069,180],{"class":123},[40,1071,1072],{"class":70},", key: ",[40,1074,180],{"class":123},[40,1076,183],{"class":70},[40,1078,914],{"class":123},[40,1080,117],{"class":70},[40,1082,1083],{"class":42,"line":330},[40,1084,1085],{"class":193},"    \"\"\"Module-level cache — no instance reference, bounded by maxsize.\"\"\"\n",[40,1087,1088,1090,1092,1094,1096,1098,1100,1103,1105,1107,1109,1111,1113,1115,1117,1119],{"class":42,"line":335},[40,1089,366],{"class":66},[40,1091,929],{"class":123},[40,1093,877],{"class":70},[40,1095,934],{"class":66},[40,1097,937],{"class":193},[40,1099,952],{"class":123},[40,1101,1102],{"class":70},"dataset_id",[40,1104,946],{"class":123},[40,1106,949],{"class":193},[40,1108,952],{"class":123},[40,1110,710],{"class":70},[40,1112,946],{"class":123},[40,1114,937],{"class":193},[40,1116,961],{"class":70},[40,1118,964],{"class":66},[40,1120,967],{"class":123},[40,1122,1123],{"class":42,"line":341},[40,1124,60],{"emptyLinePlaceholder":59},[40,1126,1127,1129,1132],{"class":42,"line":358},[40,1128,111],{"class":66},[40,1130,1131],{"class":104}," ExpensiveServiceFixed",[40,1133,117],{"class":70},[40,1135,1136,1138,1140,1142,1144],{"class":42,"line":363},[40,1137,841],{"class":66},[40,1139,844],{"class":123},[40,1141,847],{"class":70},[40,1143,180],{"class":123},[40,1145,852],{"class":70},[40,1147,1148,1150,1152,1154],{"class":42,"line":372},[40,1149,857],{"class":123},[40,1151,860],{"class":70},[40,1153,223],{"class":66},[40,1155,865],{"class":70},[40,1157,1158],{"class":42,"line":386},[40,1159,60],{"emptyLinePlaceholder":59},[40,1161,1162,1164,1166,1168,1170,1172,1174],{"class":42,"line":409},[40,1163,841],{"class":66},[40,1165,904],{"class":104},[40,1167,907],{"class":70},[40,1169,180],{"class":123},[40,1171,183],{"class":70},[40,1173,914],{"class":123},[40,1175,117],{"class":70},[40,1177,1178,1180,1183,1186,1189],{"class":42,"line":433},[40,1179,926],{"class":66},[40,1181,1182],{"class":70}," _compute_cached(",[40,1184,1185],{"class":123},"self",[40,1187,1188],{"class":70},".dataset_id, key)   ",[40,1190,1191],{"class":46},"# delegate to module-level cache\n",[40,1193,1194],{"class":42,"line":446},[40,1195,60],{"emptyLinePlaceholder":59},[40,1197,1198],{"class":42,"line":452},[40,1199,1200],{"class":46},"# Now instances are freely collectable — the cache only holds (str, str) -> float\n",[28,1202,1203],{"language":30},[32,1204,1206],{"className":34,"code":1205,"language":30,"meta":36,"style":36},"# Discouraged — assigning a lambda to a name; just use def\nbad = lambda x, y: x + y\n\n# Preferred — a def with the same behavior gets a real name and can have a docstring\ndef add(x, y):\n    \"\"\"Return the sum of x and y.\"\"\"\n    return x + y\n",[19,1207,1208,1213,1232,1236,1241,1251,1256],{"__ignoreMap":36},[40,1209,1210],{"class":42,"line":43},[40,1211,1212],{"class":46},"# Discouraged — assigning a lambda to a name; just use def\n",[40,1214,1215,1218,1220,1223,1226,1229],{"class":42,"line":50},[40,1216,1217],{"class":70},"bad ",[40,1219,223],{"class":66},[40,1221,1222],{"class":66}," lambda",[40,1224,1225],{"class":70}," x, y: x ",[40,1227,1228],{"class":66},"+",[40,1230,1231],{"class":70}," y\n",[40,1233,1234],{"class":42,"line":56},[40,1235,60],{"emptyLinePlaceholder":59},[40,1237,1238],{"class":42,"line":63},[40,1239,1240],{"class":46},"# Preferred — a def with the same behavior gets a real name and can have a docstring\n",[40,1242,1243,1245,1248],{"class":42,"line":74},[40,1244,171],{"class":66},[40,1246,1247],{"class":104}," add",[40,1249,1250],{"class":70},"(x, y):\n",[40,1252,1253],{"class":42,"line":82},[40,1254,1255],{"class":193},"    \"\"\"Return the sum of x and y.\"\"\"\n",[40,1257,1258,1260,1263,1265],{"class":42,"line":96},[40,1259,366],{"class":66},[40,1261,1262],{"class":70}," x ",[40,1264,1228],{"class":66},[40,1266,1231],{"class":70},[14,1268,1270,488,1273,1276],{"id":1269},"map-filter-and-why-comprehensions-usually-win",[19,1271,1272],{},"map",[19,1274,1275],{},"filter",", and Why Comprehensions Usually Win",[28,1278,1279],{"language":30},[32,1280,1282],{"className":34,"code":1281,"language":30,"meta":36,"style":36},"numbers = [1, 2, 3, 4, 5, 6]\n\n# map — apply a function to every element (returns a lazy iterator, like a generator)\ndoubled = map(lambda x: x * 2, numbers)\nprint(list(doubled))   # [2, 4, 6, 8, 10, 12]\n\n# filter — keep elements where the function returns truthy\nevens = filter(lambda x: x % 2 == 0, numbers)\nprint(list(evens))       # [2, 4, 6]\n\n# The equivalent, generally preferred, comprehension forms:\ndoubled_comp = [x * 2 for x in numbers]\nevens_comp = [x for x in numbers if x % 2 == 0]\n",[19,1283,1284,1321,1325,1330,1356,1371,1375,1380,1407,1421,1425,1430,1454],{"__ignoreMap":36},[40,1285,1286,1289,1291,1294,1296,1298,1300,1302,1304,1306,1309,1311,1314,1316,1319],{"class":42,"line":43},[40,1287,1288],{"class":70},"numbers ",[40,1290,223],{"class":66},[40,1292,1293],{"class":70}," [",[40,1295,474],{"class":123},[40,1297,488],{"class":70},[40,1299,504],{"class":123},[40,1301,488],{"class":70},[40,1303,562],{"class":123},[40,1305,488],{"class":70},[40,1307,1308],{"class":123},"4",[40,1310,488],{"class":70},[40,1312,1313],{"class":123},"5",[40,1315,488],{"class":70},[40,1317,1318],{"class":123},"6",[40,1320,600],{"class":70},[40,1322,1323],{"class":42,"line":50},[40,1324,60],{"emptyLinePlaceholder":59},[40,1326,1327],{"class":42,"line":56},[40,1328,1329],{"class":46},"# map — apply a function to every element (returns a lazy iterator, like a generator)\n",[40,1331,1332,1335,1337,1340,1342,1345,1348,1350,1353],{"class":42,"line":63},[40,1333,1334],{"class":70},"doubled ",[40,1336,223],{"class":66},[40,1338,1339],{"class":123}," map",[40,1341,877],{"class":70},[40,1343,1344],{"class":66},"lambda",[40,1346,1347],{"class":70}," x: x ",[40,1349,290],{"class":66},[40,1351,1352],{"class":123}," 2",[40,1354,1355],{"class":70},", numbers)\n",[40,1357,1358,1360,1362,1365,1368],{"class":42,"line":74},[40,1359,606],{"class":123},[40,1361,877],{"class":70},[40,1363,1364],{"class":123},"list",[40,1366,1367],{"class":70},"(doubled))   ",[40,1369,1370],{"class":46},"# [2, 4, 6, 8, 10, 12]\n",[40,1372,1373],{"class":42,"line":82},[40,1374,60],{"emptyLinePlaceholder":59},[40,1376,1377],{"class":42,"line":96},[40,1378,1379],{"class":46},"# filter — keep elements where the function returns truthy\n",[40,1381,1382,1385,1387,1390,1392,1394,1396,1398,1400,1403,1405],{"class":42,"line":101},[40,1383,1384],{"class":70},"evens ",[40,1386,223],{"class":66},[40,1388,1389],{"class":123}," filter",[40,1391,877],{"class":70},[40,1393,1344],{"class":66},[40,1395,1347],{"class":70},[40,1397,964],{"class":66},[40,1399,1352],{"class":123},[40,1401,1402],{"class":66}," ==",[40,1404,403],{"class":123},[40,1406,1355],{"class":70},[40,1408,1409,1411,1413,1415,1418],{"class":42,"line":108},[40,1410,606],{"class":123},[40,1412,877],{"class":70},[40,1414,1364],{"class":123},[40,1416,1417],{"class":70},"(evens))       ",[40,1419,1420],{"class":46},"# [2, 4, 6]\n",[40,1422,1423],{"class":42,"line":120},[40,1424,60],{"emptyLinePlaceholder":59},[40,1426,1427],{"class":42,"line":133},[40,1428,1429],{"class":46},"# The equivalent, generally preferred, comprehension forms:\n",[40,1431,1432,1435,1437,1440,1442,1444,1447,1449,1451],{"class":42,"line":148},[40,1433,1434],{"class":70},"doubled_comp ",[40,1436,223],{"class":66},[40,1438,1439],{"class":70}," [x ",[40,1441,290],{"class":66},[40,1443,1352],{"class":123},[40,1445,1446],{"class":66}," for",[40,1448,1262],{"class":70},[40,1450,235],{"class":66},[40,1452,1453],{"class":70}," numbers]\n",[40,1455,1456,1459,1461,1463,1465,1467,1469,1472,1474,1476,1478,1480,1482,1484],{"class":42,"line":157},[40,1457,1458],{"class":70},"evens_comp ",[40,1460,223],{"class":66},[40,1462,1439],{"class":70},[40,1464,229],{"class":66},[40,1466,1262],{"class":70},[40,1468,235],{"class":66},[40,1470,1471],{"class":70}," numbers ",[40,1473,241],{"class":66},[40,1475,1262],{"class":70},[40,1477,964],{"class":66},[40,1479,1352],{"class":123},[40,1481,1402],{"class":66},[40,1483,403],{"class":123},[40,1485,600],{"class":70},[1487,1488,1489,1490,1492,1493,1495,1496,1492,1498,1500,1501,1505],"p",{},"Python's own style guide and most experienced Python developers prefer comprehensions over ",[19,1491,1272],{},"\u002F",[19,1494,1275],{}," with lambdas — they read left-to-right in plain English (\"x times 2 for x in numbers\") rather than requiring you to mentally unwrap nested function calls. ",[19,1497,1272],{},[19,1499,1275],{}," remain useful when you already have a ",[1502,1503,1504],"strong",{},"named"," function (no lambda needed) to pass directly:",[28,1507,1508],{"language":30},[32,1509,1511],{"className":34,"code":1510,"language":30,"meta":36,"style":36},"# Genuinely clean use of map — no lambda, an existing named function\nstrings = [\"1\", \"2\", \"3\"]\nnumbers = list(map(int, strings))\nprint(numbers)   # [1, 2, 3]\n\n# vs. the comprehension equivalent — about equally readable here\nnumbers_comp = [int(s) for s in strings]\n",[19,1512,1513,1518,1542,1562,1572,1576,1581],{"__ignoreMap":36},[40,1514,1515],{"class":42,"line":43},[40,1516,1517],{"class":46},"# Genuinely clean use of map — no lambda, an existing named function\n",[40,1519,1520,1523,1525,1527,1530,1532,1535,1537,1540],{"class":42,"line":50},[40,1521,1522],{"class":70},"strings ",[40,1524,223],{"class":66},[40,1526,1293],{"class":70},[40,1528,1529],{"class":193},"\"1\"",[40,1531,488],{"class":70},[40,1533,1534],{"class":193},"\"2\"",[40,1536,488],{"class":70},[40,1538,1539],{"class":193},"\"3\"",[40,1541,600],{"class":70},[40,1543,1544,1546,1548,1550,1552,1554,1556,1559],{"class":42,"line":56},[40,1545,1288],{"class":70},[40,1547,223],{"class":66},[40,1549,349],{"class":123},[40,1551,877],{"class":70},[40,1553,1272],{"class":123},[40,1555,877],{"class":70},[40,1557,1558],{"class":123},"int",[40,1560,1561],{"class":70},", strings))\n",[40,1563,1564,1566,1569],{"class":42,"line":63},[40,1565,606],{"class":123},[40,1567,1568],{"class":70},"(numbers)   ",[40,1570,1571],{"class":46},"# [1, 2, 3]\n",[40,1573,1574],{"class":42,"line":74},[40,1575,60],{"emptyLinePlaceholder":59},[40,1577,1578],{"class":42,"line":82},[40,1579,1580],{"class":46},"# vs. the comprehension equivalent — about equally readable here\n",[40,1582,1583,1586,1588,1590,1592,1595,1597,1600,1602],{"class":42,"line":96},[40,1584,1585],{"class":70},"numbers_comp ",[40,1587,223],{"class":66},[40,1589,1293],{"class":70},[40,1591,1558],{"class":123},[40,1593,1594],{"class":70},"(s) ",[40,1596,229],{"class":66},[40,1598,1599],{"class":70}," s ",[40,1601,235],{"class":66},[40,1603,1604],{"class":70}," strings]\n",[14,1606,1608,1611],{"id":1607},"functoolsreduce-fold-a-sequence-into-a-single-value",[19,1609,1610],{},"functools.reduce"," — Fold a Sequence Into a Single Value",[28,1613,1614],{"language":30},[32,1615,1617],{"className":34,"code":1616,"language":30,"meta":36,"style":36},"from functools import reduce\n\nnumbers = [1, 2, 3, 4, 5]\n\ntotal = reduce(lambda acc, x: acc + x, numbers)\nprint(total)   # 15 — equivalent to sum(numbers), shown here for illustration\n\nproduct = reduce(lambda acc, x: acc * x, numbers, 1)   # 1 is the initial accumulator\nprint(product)   # 120\n\n# A more realistic use: reducing to a non-numeric structure\nwords = [\"the\", \"quick\", \"brown\", \"fox\"]\nlongest = reduce(lambda a, b: a if len(a) >= len(b) else b, words)\nprint(longest)   # \"quick\" (first of the tied-longest, due to >=)\n",[19,1618,1619,1630,1634,1662,1666,1688,1698,1702,1729,1739,1743,1748,1777,1814],{"__ignoreMap":36},[40,1620,1621,1623,1625,1627],{"class":42,"line":43},[40,1622,85],{"class":66},[40,1624,811],{"class":70},[40,1626,67],{"class":66},[40,1628,1629],{"class":709}," reduce\n",[40,1631,1632],{"class":42,"line":50},[40,1633,60],{"emptyLinePlaceholder":59},[40,1635,1636,1638,1640,1642,1644,1646,1648,1650,1652,1654,1656,1658,1660],{"class":42,"line":56},[40,1637,1288],{"class":70},[40,1639,223],{"class":66},[40,1641,1293],{"class":70},[40,1643,474],{"class":123},[40,1645,488],{"class":70},[40,1647,504],{"class":123},[40,1649,488],{"class":70},[40,1651,562],{"class":123},[40,1653,488],{"class":70},[40,1655,1308],{"class":123},[40,1657,488],{"class":70},[40,1659,1313],{"class":123},[40,1661,600],{"class":70},[40,1663,1664],{"class":42,"line":63},[40,1665,60],{"emptyLinePlaceholder":59},[40,1667,1668,1671,1673,1676,1678,1680,1683,1685],{"class":42,"line":74},[40,1669,1670],{"class":70},"total ",[40,1672,223],{"class":66},[40,1674,1675],{"class":709}," reduce",[40,1677,877],{"class":70},[40,1679,1344],{"class":66},[40,1681,1682],{"class":70}," acc, x: acc ",[40,1684,1228],{"class":66},[40,1686,1687],{"class":70}," x, numbers)\n",[40,1689,1690,1692,1695],{"class":42,"line":82},[40,1691,606],{"class":123},[40,1693,1694],{"class":70},"(total)   ",[40,1696,1697],{"class":46},"# 15 — equivalent to sum(numbers), shown here for illustration\n",[40,1699,1700],{"class":42,"line":96},[40,1701,60],{"emptyLinePlaceholder":59},[40,1703,1704,1707,1709,1711,1713,1715,1717,1719,1722,1724,1726],{"class":42,"line":101},[40,1705,1706],{"class":70},"product ",[40,1708,223],{"class":66},[40,1710,1675],{"class":709},[40,1712,877],{"class":70},[40,1714,1344],{"class":66},[40,1716,1682],{"class":70},[40,1718,290],{"class":66},[40,1720,1721],{"class":70}," x, numbers, ",[40,1723,474],{"class":123},[40,1725,888],{"class":70},[40,1727,1728],{"class":46},"# 1 is the initial accumulator\n",[40,1730,1731,1733,1736],{"class":42,"line":108},[40,1732,606],{"class":123},[40,1734,1735],{"class":70},"(product)   ",[40,1737,1738],{"class":46},"# 120\n",[40,1740,1741],{"class":42,"line":120},[40,1742,60],{"emptyLinePlaceholder":59},[40,1744,1745],{"class":42,"line":133},[40,1746,1747],{"class":46},"# A more realistic use: reducing to a non-numeric structure\n",[40,1749,1750,1753,1755,1757,1760,1762,1765,1767,1770,1772,1775],{"class":42,"line":148},[40,1751,1752],{"class":70},"words ",[40,1754,223],{"class":66},[40,1756,1293],{"class":70},[40,1758,1759],{"class":193},"\"the\"",[40,1761,488],{"class":70},[40,1763,1764],{"class":193},"\"quick\"",[40,1766,488],{"class":70},[40,1768,1769],{"class":193},"\"brown\"",[40,1771,488],{"class":70},[40,1773,1774],{"class":193},"\"fox\"",[40,1776,600],{"class":70},[40,1778,1779,1782,1784,1786,1788,1790,1793,1795,1798,1801,1804,1806,1809,1811],{"class":42,"line":157},[40,1780,1781],{"class":70},"longest ",[40,1783,223],{"class":66},[40,1785,1675],{"class":709},[40,1787,877],{"class":70},[40,1789,1344],{"class":66},[40,1791,1792],{"class":70}," a, b: a ",[40,1794,241],{"class":66},[40,1796,1797],{"class":123}," len",[40,1799,1800],{"class":70},"(a) ",[40,1802,1803],{"class":66},">=",[40,1805,1797],{"class":123},[40,1807,1808],{"class":70},"(b) ",[40,1810,400],{"class":66},[40,1812,1813],{"class":70}," b, words)\n",[40,1815,1816,1818,1821],{"class":42,"line":162},[40,1817,606],{"class":123},[40,1819,1820],{"class":70},"(longest)   ",[40,1822,1823],{"class":46},"# \"quick\" (first of the tied-longest, due to >=)\n",[1487,1825,1826,1827,1830,1831,488,1833,488,1836,1492,1838,1841,1842,1844,1845,1847,1848,1851,1852,488,1854,488,1857,488,1860,1862],{},"Guido van Rossum has been on record disliking ",[19,1828,1829],{},"reduce"," for exactly the reason shown above: nested lambda-based folds read poorly compared to an explicit loop or a purpose-built function (",[19,1832,441],{},[19,1834,1835],{},"math.prod",[19,1837,417],{},[19,1839,1840],{},"min"," with ",[19,1843,710],{},"). ",[19,1846,1829],{}," was demoted from a builtin to ",[19,1849,1850],{},"functools"," in Python 3 for this reason. Use it when there's genuinely no built-in equivalent; otherwise prefer the specific tool (",[19,1853,441],{},[19,1855,1856],{},"any",[19,1858,1859],{},"all",[19,1861,1835],{},").",[14,1864,1866,1868],{"id":1865},"functoolspartial-pre-filling-arguments",[19,1867,21],{}," — Pre-Filling Arguments",[28,1870,1871],{"language":30},[32,1872,1874],{"className":34,"code":1873,"language":30,"meta":36,"style":36},"from functools import partial\n\ndef power(base, exponent):\n    return base ** exponent\n\nsquare = partial(power, exponent=2)\ncube = partial(power, exponent=3)\n\nprint(square(5))   # 25\nprint(cube(5))        # 125\n\n# Real-world use: adapting a callback's signature to what an API expects\nimport logging\n\nlog_error = partial(logging.log, logging.ERROR)\nlog_error(\"Something broke: %s\", \"disk full\")\n",[19,1875,1876,1887,1891,1901,1914,1918,1937,1954,1958,1973,1988,1992,1997,2004,2008,2023],{"__ignoreMap":36},[40,1877,1878,1880,1882,1884],{"class":42,"line":43},[40,1879,85],{"class":66},[40,1881,811],{"class":70},[40,1883,67],{"class":66},[40,1885,1886],{"class":70}," partial\n",[40,1888,1889],{"class":42,"line":50},[40,1890,60],{"emptyLinePlaceholder":59},[40,1892,1893,1895,1898],{"class":42,"line":56},[40,1894,171],{"class":66},[40,1896,1897],{"class":104}," power",[40,1899,1900],{"class":70},"(base, exponent):\n",[40,1902,1903,1905,1908,1911],{"class":42,"line":63},[40,1904,366],{"class":66},[40,1906,1907],{"class":70}," base ",[40,1909,1910],{"class":66},"**",[40,1912,1913],{"class":70}," exponent\n",[40,1915,1916],{"class":42,"line":74},[40,1917,60],{"emptyLinePlaceholder":59},[40,1919,1920,1923,1925,1928,1931,1933,1935],{"class":42,"line":82},[40,1921,1922],{"class":70},"square ",[40,1924,223],{"class":66},[40,1926,1927],{"class":70}," partial(power, ",[40,1929,1930],{"class":709},"exponent",[40,1932,223],{"class":66},[40,1934,504],{"class":123},[40,1936,992],{"class":70},[40,1938,1939,1942,1944,1946,1948,1950,1952],{"class":42,"line":96},[40,1940,1941],{"class":70},"cube ",[40,1943,223],{"class":66},[40,1945,1927],{"class":70},[40,1947,1930],{"class":709},[40,1949,223],{"class":66},[40,1951,562],{"class":123},[40,1953,992],{"class":70},[40,1955,1956],{"class":42,"line":101},[40,1957,60],{"emptyLinePlaceholder":59},[40,1959,1960,1962,1965,1967,1970],{"class":42,"line":108},[40,1961,606],{"class":123},[40,1963,1964],{"class":70},"(square(",[40,1966,1313],{"class":123},[40,1968,1969],{"class":70},"))   ",[40,1971,1972],{"class":46},"# 25\n",[40,1974,1975,1977,1980,1982,1985],{"class":42,"line":120},[40,1976,606],{"class":123},[40,1978,1979],{"class":70},"(cube(",[40,1981,1313],{"class":123},[40,1983,1984],{"class":70},"))        ",[40,1986,1987],{"class":46},"# 125\n",[40,1989,1990],{"class":42,"line":133},[40,1991,60],{"emptyLinePlaceholder":59},[40,1993,1994],{"class":42,"line":148},[40,1995,1996],{"class":46},"# Real-world use: adapting a callback's signature to what an API expects\n",[40,1998,1999,2001],{"class":42,"line":157},[40,2000,67],{"class":66},[40,2002,2003],{"class":70}," logging\n",[40,2005,2006],{"class":42,"line":162},[40,2007,60],{"emptyLinePlaceholder":59},[40,2009,2010,2013,2015,2018,2021],{"class":42,"line":168},[40,2011,2012],{"class":70},"log_error ",[40,2014,223],{"class":66},[40,2016,2017],{"class":70}," partial(logging.log, logging.",[40,2019,2020],{"class":123},"ERROR",[40,2022,992],{"class":70},[40,2024,2025,2028,2031,2034,2036,2038,2041],{"class":42,"line":190},[40,2026,2027],{"class":70},"log_error(",[40,2029,2030],{"class":193},"\"Something broke: ",[40,2032,2033],{"class":123},"%s",[40,2035,937],{"class":193},[40,2037,488],{"class":70},[40,2039,2040],{"class":193},"\"disk full\"",[40,2042,992],{"class":70},[1487,2044,2045,2048,2049,488,2052,2055,2056,2058,2059,2061],{},[19,2046,2047],{},"partial"," is especially useful when passing callbacks into APIs (GUI frameworks, ",[19,2050,2051],{},"signal.signal",[19,2053,2054],{},"concurrent.futures.Executor.submit",") that expect a fixed-arity callable but you need to bake in extra context without wrapping in a full ",[19,2057,1344],{}," or nested ",[19,2060,171],{},".",[14,2063,2065,2068],{"id":2064},"functoolslru_cache-memoization-in-one-line",[19,2066,2067],{},"functools.lru_cache"," — Memoization in One Line",[28,2070,2071],{"language":30},[32,2072,2074],{"className":34,"code":2073,"language":30,"meta":36,"style":36},"from functools import lru_cache\nimport time\n\n@lru_cache(maxsize=None)\ndef fibonacci(n):\n    if n \u003C 2:\n        return n\n    return fibonacci(n - 1) + fibonacci(n - 2)\n\nstart = time.perf_counter()\nprint(fibonacci(35))              # fast — cached subresults\nprint(f\"{time.perf_counter() - start:.4f}s\")\n\nprint(fibonacci.cache_info())       # CacheInfo(hits=33, misses=36, maxsize=None, currsize=36)\nfibonacci.cache_clear()                # wipe the cache manually if needed\n",[19,2075,2076,2086,2093,2097,2111,2121,2136,2143,2168,2172,2182,2198,2228,2232,2242],{"__ignoreMap":36},[40,2077,2078,2080,2082,2084],{"class":42,"line":43},[40,2079,85],{"class":66},[40,2081,811],{"class":70},[40,2083,67],{"class":66},[40,2085,816],{"class":70},[40,2087,2088,2090],{"class":42,"line":50},[40,2089,67],{"class":66},[40,2091,2092],{"class":70}," time\n",[40,2094,2095],{"class":42,"line":56},[40,2096,60],{"emptyLinePlaceholder":59},[40,2098,2099,2101,2103,2105,2107,2109],{"class":42,"line":63},[40,2100,1046],{"class":104},[40,2102,877],{"class":70},[40,2104,880],{"class":709},[40,2106,223],{"class":66},[40,2108,885],{"class":123},[40,2110,992],{"class":70},[40,2112,2113,2115,2118],{"class":42,"line":74},[40,2114,171],{"class":66},[40,2116,2117],{"class":104}," fibonacci",[40,2119,2120],{"class":70},"(n):\n",[40,2122,2123,2126,2129,2132,2134],{"class":42,"line":82},[40,2124,2125],{"class":66},"    if",[40,2127,2128],{"class":70}," n ",[40,2130,2131],{"class":66},"\u003C",[40,2133,1352],{"class":123},[40,2135,117],{"class":70},[40,2137,2138,2140],{"class":42,"line":96},[40,2139,926],{"class":66},[40,2141,2142],{"class":70}," n\n",[40,2144,2145,2147,2150,2153,2156,2158,2160,2162,2164,2166],{"class":42,"line":101},[40,2146,366],{"class":66},[40,2148,2149],{"class":70}," fibonacci(n ",[40,2151,2152],{"class":66},"-",[40,2154,2155],{"class":123}," 1",[40,2157,961],{"class":70},[40,2159,1228],{"class":66},[40,2161,2149],{"class":70},[40,2163,2152],{"class":66},[40,2165,1352],{"class":123},[40,2167,992],{"class":70},[40,2169,2170],{"class":42,"line":108},[40,2171,60],{"emptyLinePlaceholder":59},[40,2173,2174,2177,2179],{"class":42,"line":120},[40,2175,2176],{"class":70},"start ",[40,2178,223],{"class":66},[40,2180,2181],{"class":70}," time.perf_counter()\n",[40,2183,2184,2186,2189,2192,2195],{"class":42,"line":133},[40,2185,606],{"class":123},[40,2187,2188],{"class":70},"(fibonacci(",[40,2190,2191],{"class":123},"35",[40,2193,2194],{"class":70},"))              ",[40,2196,2197],{"class":46},"# fast — cached subresults\n",[40,2199,2200,2202,2204,2206,2208,2210,2213,2215,2218,2221,2223,2226],{"class":42,"line":148},[40,2201,606],{"class":123},[40,2203,877],{"class":70},[40,2205,934],{"class":66},[40,2207,937],{"class":193},[40,2209,952],{"class":123},[40,2211,2212],{"class":70},"time.perf_counter() ",[40,2214,2152],{"class":66},[40,2216,2217],{"class":70}," start",[40,2219,2220],{"class":66},":.4f",[40,2222,946],{"class":123},[40,2224,2225],{"class":193},"s\"",[40,2227,992],{"class":70},[40,2229,2230],{"class":42,"line":157},[40,2231,60],{"emptyLinePlaceholder":59},[40,2233,2234,2236,2239],{"class":42,"line":162},[40,2235,606],{"class":123},[40,2237,2238],{"class":70},"(fibonacci.cache_info())       ",[40,2240,2241],{"class":46},"# CacheInfo(hits=33, misses=36, maxsize=None, currsize=36)\n",[40,2243,2244,2247],{"class":42,"line":168},[40,2245,2246],{"class":70},"fibonacci.cache_clear()                ",[40,2248,2249],{"class":46},"# wipe the cache manually if needed\n",[1487,2251,2252,2253,2255,2256,2259,2260,2263,2264,2266,2267,1862],{},"Without ",[19,2254,1046],{},", naive recursive Fibonacci is O(2ⁿ) — recomputing the same subproblems exponentially many times. ",[19,2257,2258],{},"lru_cache"," turns it into O(n) by remembering every unique ",[19,2261,2262],{},"(args) -> result"," pair it has already computed, at the cost of memory proportional to the number of distinct calls (bounded by ",[19,2265,880],{},", or unbounded if ",[19,2268,2269],{},"maxsize=None",[2271,2272,2274,2275,2277],"h3",{"id":2273},"the-gotcha-lru_cache-requires-hashable-arguments-and-ignores-default-vs-explicit-distinctions-loosely","The gotcha: ",[19,2276,2258],{}," requires hashable arguments, and ignores default-vs-explicit distinctions loosely",[28,2279,2280],{"language":30},[32,2281,2283],{"className":34,"code":2282,"language":30,"meta":36,"style":36},"@lru_cache(maxsize=None)\ndef process(items):\n    return sum(items)\n\n# process([1, 2, 3])   # TypeError: unhashable type: 'list'\nprocess((1, 2, 3))       # OK — tuples are hashable\n",[19,2284,2285,2299,2309,2319,2323,2328],{"__ignoreMap":36},[40,2286,2287,2289,2291,2293,2295,2297],{"class":42,"line":43},[40,2288,1046],{"class":104},[40,2290,877],{"class":70},[40,2292,880],{"class":709},[40,2294,223],{"class":66},[40,2296,885],{"class":123},[40,2298,992],{"class":70},[40,2300,2301,2303,2306],{"class":42,"line":50},[40,2302,171],{"class":66},[40,2304,2305],{"class":104}," process",[40,2307,2308],{"class":70},"(items):\n",[40,2310,2311,2313,2316],{"class":42,"line":56},[40,2312,366],{"class":66},[40,2314,2315],{"class":123}," sum",[40,2317,2318],{"class":70},"(items)\n",[40,2320,2321],{"class":42,"line":63},[40,2322,60],{"emptyLinePlaceholder":59},[40,2324,2325],{"class":42,"line":74},[40,2326,2327],{"class":46},"# process([1, 2, 3])   # TypeError: unhashable type: 'list'\n",[40,2329,2330,2333,2335,2337,2339,2341,2343,2346],{"class":42,"line":82},[40,2331,2332],{"class":70},"process((",[40,2334,474],{"class":123},[40,2336,488],{"class":70},[40,2338,504],{"class":123},[40,2340,488],{"class":70},[40,2342,562],{"class":123},[40,2344,2345],{"class":70},"))       ",[40,2347,2348],{"class":46},"# OK — tuples are hashable\n",[2271,2350,2352],{"id":2351},"the-gotcha-caching-methods-on-instances-leaks-memory","The gotcha: caching methods on instances leaks memory",[28,2354,2355],{"language":30},[32,2356,2358],{"className":34,"code":2357,"language":30,"meta":36,"style":36},"class Report:\n    def __init__(self, data):\n        self.data = data\n\n    @lru_cache(maxsize=None)         # DANGEROUS on an instance method!\n    def summary(self):\n        return sum(self.data)\n",[19,2359,2360,2369,2378,2390,2394,2412,2422],{"__ignoreMap":36},[40,2361,2362,2364,2367],{"class":42,"line":43},[40,2363,111],{"class":66},[40,2365,2366],{"class":104}," Report",[40,2368,117],{"class":70},[40,2370,2371,2373,2375],{"class":42,"line":50},[40,2372,841],{"class":66},[40,2374,844],{"class":123},[40,2376,2377],{"class":70},"(self, data):\n",[40,2379,2380,2382,2385,2387],{"class":42,"line":56},[40,2381,857],{"class":123},[40,2383,2384],{"class":70},".data ",[40,2386,223],{"class":66},[40,2388,2389],{"class":70}," data\n",[40,2391,2392],{"class":42,"line":63},[40,2393,60],{"emptyLinePlaceholder":59},[40,2395,2396,2398,2400,2402,2404,2406,2409],{"class":42,"line":74},[40,2397,874],{"class":104},[40,2399,877],{"class":70},[40,2401,880],{"class":709},[40,2403,223],{"class":66},[40,2405,885],{"class":123},[40,2407,2408],{"class":70},")         ",[40,2410,2411],{"class":46},"# DANGEROUS on an instance method!\n",[40,2413,2414,2416,2419],{"class":42,"line":82},[40,2415,841],{"class":66},[40,2417,2418],{"class":104}," summary",[40,2420,2421],{"class":70},"(self):\n",[40,2423,2424,2426,2428,2430,2432],{"class":42,"line":96},[40,2425,926],{"class":66},[40,2427,2315],{"class":123},[40,2429,877],{"class":70},[40,2431,1185],{"class":123},[40,2433,2434],{"class":70},".data)\n",[1487,2436,2437,2439,2440,2443,2444,2446,2447,2450,2451,2454],{},[19,2438,1046],{}," on an instance method caches keyed on ",[19,2441,2442],{},"(self, ...)"," — since ",[19,2445,1185],{}," is part of the cache key, ",[1502,2448,2449],{},"every distinct instance that ever calls this method stays alive forever",", held by a strong reference inside the cache, even after all other references to it are dropped. This is a real, subtle memory leak pattern seen in production code. The fix is typically to cache a module-level function that takes only hashable, non-instance arguments, or to use a bespoke per-instance cache (e.g., ",[19,2452,2453],{},"functools.cached_property",", covered in chapter 15) instead.",[14,2456,2458,2461],{"id":2457},"functoolswraps-preserving-metadata-through-decorators",[19,2459,2460],{},"functools.wraps"," — Preserving Metadata Through Decorators",[28,2463,2464],{"language":30},[32,2465,2467],{"className":34,"code":2466,"language":30,"meta":36,"style":36},"from functools import wraps\n\ndef logged(func):\n    @wraps(func)          # copies __name__, __doc__, __module__ from func onto wrapper\n    def wrapper(*args, **kwargs):\n        print(f\"Calling {func.__name__}\")\n        return func(*args, **kwargs)\n    return wrapper\n\n@logged\ndef greet(name):\n    \"\"\"Return a greeting.\"\"\"\n    return f\"Hello, {name}!\"\n\nprint(greet.__name__)   # \"greet\" — WITH @wraps\nprint(greet.__doc__)      # \"Return a greeting.\" — WITH @wraps\n\n# Without @wraps, both would incorrectly report \"wrapper\" and None respectively —\n# breaking introspection, help(), and any tooling that inspects function metadata.\n",[19,2468,2469,2480,2484,2494,2505,2524,2548,2564,2571,2575,2580,2590,2595,2615,2619,2634,2649,2653,2658],{"__ignoreMap":36},[40,2470,2471,2473,2475,2477],{"class":42,"line":43},[40,2472,85],{"class":66},[40,2474,811],{"class":70},[40,2476,67],{"class":66},[40,2478,2479],{"class":70}," wraps\n",[40,2481,2482],{"class":42,"line":50},[40,2483,60],{"emptyLinePlaceholder":59},[40,2485,2486,2488,2491],{"class":42,"line":56},[40,2487,171],{"class":66},[40,2489,2490],{"class":104}," logged",[40,2492,2493],{"class":70},"(func):\n",[40,2495,2496,2499,2502],{"class":42,"line":63},[40,2497,2498],{"class":104},"    @wraps",[40,2500,2501],{"class":70},"(func)          ",[40,2503,2504],{"class":46},"# copies __name__, __doc__, __module__ from func onto wrapper\n",[40,2506,2507,2509,2512,2514,2516,2519,2521],{"class":42,"line":74},[40,2508,841],{"class":66},[40,2510,2511],{"class":104}," wrapper",[40,2513,877],{"class":70},[40,2515,290],{"class":66},[40,2517,2518],{"class":70},"args, ",[40,2520,1910],{"class":66},[40,2522,2523],{"class":70},"kwargs):\n",[40,2525,2526,2529,2531,2533,2536,2538,2541,2544,2546],{"class":42,"line":82},[40,2527,2528],{"class":123},"        print",[40,2530,877],{"class":70},[40,2532,934],{"class":66},[40,2534,2535],{"class":193},"\"Calling ",[40,2537,952],{"class":123},[40,2539,2540],{"class":70},"func.",[40,2542,2543],{"class":123},"__name__}",[40,2545,937],{"class":193},[40,2547,992],{"class":70},[40,2549,2550,2552,2555,2557,2559,2561],{"class":42,"line":96},[40,2551,926],{"class":66},[40,2553,2554],{"class":70}," func(",[40,2556,290],{"class":66},[40,2558,2518],{"class":70},[40,2560,1910],{"class":66},[40,2562,2563],{"class":70},"kwargs)\n",[40,2565,2566,2568],{"class":42,"line":101},[40,2567,366],{"class":66},[40,2569,2570],{"class":70}," wrapper\n",[40,2572,2573],{"class":42,"line":108},[40,2574,60],{"emptyLinePlaceholder":59},[40,2576,2577],{"class":42,"line":120},[40,2578,2579],{"class":104},"@logged\n",[40,2581,2582,2584,2587],{"class":42,"line":133},[40,2583,171],{"class":66},[40,2585,2586],{"class":104}," greet",[40,2588,2589],{"class":70},"(name):\n",[40,2591,2592],{"class":42,"line":148},[40,2593,2594],{"class":193},"    \"\"\"Return a greeting.\"\"\"\n",[40,2596,2597,2599,2602,2605,2607,2610,2612],{"class":42,"line":157},[40,2598,366],{"class":66},[40,2600,2601],{"class":66}," f",[40,2603,2604],{"class":193},"\"Hello, ",[40,2606,952],{"class":123},[40,2608,2609],{"class":70},"name",[40,2611,946],{"class":123},[40,2613,2614],{"class":193},"!\"\n",[40,2616,2617],{"class":42,"line":162},[40,2618,60],{"emptyLinePlaceholder":59},[40,2620,2621,2623,2626,2629,2631],{"class":42,"line":168},[40,2622,606],{"class":123},[40,2624,2625],{"class":70},"(greet.",[40,2627,2628],{"class":123},"__name__",[40,2630,888],{"class":70},[40,2632,2633],{"class":46},"# \"greet\" — WITH @wraps\n",[40,2635,2636,2638,2640,2643,2646],{"class":42,"line":190},[40,2637,606],{"class":123},[40,2639,2625],{"class":70},[40,2641,2642],{"class":123},"__doc__",[40,2644,2645],{"class":70},")      ",[40,2647,2648],{"class":46},"# \"Return a greeting.\" — WITH @wraps\n",[40,2650,2651],{"class":42,"line":197},[40,2652,60],{"emptyLinePlaceholder":59},[40,2654,2655],{"class":42,"line":206},[40,2656,2657],{"class":46},"# Without @wraps, both would incorrectly report \"wrapper\" and None respectively —\n",[40,2659,2660],{"class":42,"line":211},[40,2661,2662],{"class":46},"# breaking introspection, help(), and any tooling that inspects function metadata.\n",[1487,2664,2665,2666,2669,2670,2672],{},"Chapter 11 covers writing decorators from scratch in depth; ",[19,2667,2668],{},"@wraps"," is introduced here because it's a ",[19,2671,1850],{}," tool, and omitting it is one of the most common decorator-authoring mistakes.",[14,2674,2676],{"id":2675},"higher-order-functions-and-function-composition","Higher-Order Functions and Function Composition",[28,2678,2679],{"language":30},[32,2680,2682],{"className":34,"code":2681,"language":30,"meta":36,"style":36},"def compose(*functions):\n    def composed(x):\n        for f in reversed(functions):\n            x = f(x)\n        return x\n    return composed\n\nadd_one = lambda x: x + 1\ndouble = lambda x: x * 2\n\npipeline = compose(double, add_one)   # double(add_one(x)) — right-to-left, math convention\nprint(pipeline(5))   # double(add_one(5)) = double(6) = 12\n",[19,2683,2684,2698,2708,2723,2733,2740,2747,2751,2767,2783,2787,2800],{"__ignoreMap":36},[40,2685,2686,2688,2691,2693,2695],{"class":42,"line":43},[40,2687,171],{"class":66},[40,2689,2690],{"class":104}," compose",[40,2692,877],{"class":70},[40,2694,290],{"class":66},[40,2696,2697],{"class":70},"functions):\n",[40,2699,2700,2702,2705],{"class":42,"line":50},[40,2701,841],{"class":66},[40,2703,2704],{"class":104}," composed",[40,2706,2707],{"class":70},"(x):\n",[40,2709,2710,2712,2715,2717,2720],{"class":42,"line":56},[40,2711,314],{"class":66},[40,2713,2714],{"class":70}," f ",[40,2716,235],{"class":66},[40,2718,2719],{"class":123}," reversed",[40,2721,2722],{"class":70},"(functions):\n",[40,2724,2725,2728,2730],{"class":42,"line":63},[40,2726,2727],{"class":70},"            x ",[40,2729,223],{"class":66},[40,2731,2732],{"class":70}," f(x)\n",[40,2734,2735,2737],{"class":42,"line":74},[40,2736,926],{"class":66},[40,2738,2739],{"class":70}," x\n",[40,2741,2742,2744],{"class":42,"line":82},[40,2743,366],{"class":66},[40,2745,2746],{"class":70}," composed\n",[40,2748,2749],{"class":42,"line":96},[40,2750,60],{"emptyLinePlaceholder":59},[40,2752,2753,2756,2758,2760,2762,2764],{"class":42,"line":101},[40,2754,2755],{"class":70},"add_one ",[40,2757,223],{"class":66},[40,2759,1222],{"class":66},[40,2761,1347],{"class":70},[40,2763,1228],{"class":66},[40,2765,2766],{"class":123}," 1\n",[40,2768,2769,2772,2774,2776,2778,2780],{"class":42,"line":108},[40,2770,2771],{"class":70},"double ",[40,2773,223],{"class":66},[40,2775,1222],{"class":66},[40,2777,1347],{"class":70},[40,2779,290],{"class":66},[40,2781,2782],{"class":123}," 2\n",[40,2784,2785],{"class":42,"line":120},[40,2786,60],{"emptyLinePlaceholder":59},[40,2788,2789,2792,2794,2797],{"class":42,"line":133},[40,2790,2791],{"class":70},"pipeline ",[40,2793,223],{"class":66},[40,2795,2796],{"class":70}," compose(double, add_one)   ",[40,2798,2799],{"class":46},"# double(add_one(x)) — right-to-left, math convention\n",[40,2801,2802,2804,2807,2809,2811],{"class":42,"line":148},[40,2803,606],{"class":123},[40,2805,2806],{"class":70},"(pipeline(",[40,2808,1313],{"class":123},[40,2810,1969],{"class":70},[40,2812,2813],{"class":46},"# double(add_one(5)) = double(6) = 12\n",[1487,2815,2816,2817,2820,2821,488,2824,2827],{},"Python doesn't have a built-in ",[19,2818,2819],{},"compose",", unlike some functional languages — this pattern is hand-rolled or imported from third-party libraries (",[19,2822,2823],{},"toolz",[19,2825,2826],{},"funcy",") when a codebase leans heavily functional.",[14,2829,2831],{"id":2830},"tips-tricks","💡 Tips & Tricks",[2833,2834,2835,2856,2877,2893,2906],"ul",{},[2836,2837,2838,2841,2842,2845,2846,2849,2850,2852,2853,2855],"li",{},[1502,2839,2840],{},"Prefer named functions over lambdas the moment logic exceeds one clear expression"," — the improved traceback (",[19,2843,2844],{},"greet"," instead of ",[19,2847,2848],{},"\u003Clambda>","), docstring support, and testability of ",[19,2851,171],{}," outweigh the brevity of ",[19,2854,1344],{}," past trivial cases.",[2836,2857,2858,2868,2869,2872,2873,2876],{},[1502,2859,2860,2862,2863,1492,2866],{},[19,2861,25],{}," module functions replace common lambdas in ",[19,2864,2865],{},"key=",[19,2867,1829],{}," — ",[19,2870,2871],{},"sorted(people, key=operator.itemgetter(\"age\"))"," and ",[19,2874,2875],{},"reduce(operator.mul, numbers, 1)"," avoid lambda overhead and read as clearly (often more so) than the lambda equivalent.",[2836,2878,2879,2888,2889,2892],{},[1502,2880,2881,2883,2884,2887],{},[19,2882,1835],{}," replaces the classic ",[19,2885,2886],{},"reduce(lambda a, b: a*b, ...)"," idiom"," — since Python 3.8, use the built-in for products the same way ",[19,2890,2891],{},"sum()"," has always existed for sums.",[2836,2894,2895,2905],{},[1502,2896,2897,2900,2901,2904],{},[19,2898,2899],{},"functools.cache"," (3.9+) is ",[19,2902,2903],{},"lru_cache(maxsize=None)"," spelled shorter"," — use it for unbounded memoization when you don't need to tune eviction.",[2836,2907,2908,2915,2916,2919,2920,2923,2924,2926],{},[1502,2909,2910,2911,2914],{},"Check ",[19,2912,2913],{},".cache_info()"," on suspiciously slow cached functions"," — a low hit ratio (",[19,2917,2918],{},"hits"," much smaller than ",[19,2921,2922],{},"misses",") usually means the arguments aren't actually repeating, and ",[19,2925,2258],{}," is providing no benefit while still paying memory\u002Fhashing overhead.",[14,2928,2930],{"id":2929},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2833,2932,2933,2955,2969,2993,3017],{},[2836,2934,2935,2940,2941,1492,2943,2945,2946,2949,2950,2954],{},[1502,2936,2937,2939],{},[19,2938,1344],{}," bodies can only be a single expression — no statements, no multiple lines of logic"," — attempting ",[19,2942,241],{},[19,2944,229],{},"\u002Fassignment as statements inside a lambda is a ",[19,2947,2948],{},"SyntaxError","; only the ternary conditional ",[2951,2952,2953],"em",{},"expression"," form is legal.",[2836,2956,2957,2962,2963,2965,2966,2968],{},[1502,2958,2959,2961],{},[19,2960,1046],{}," on instance methods keeps every instance that's ever called it alive forever"," — because ",[19,2964,1185],{}," is part of the cache key and the cache holds a strong reference, this is a real memory leak in long-running processes (web servers, workers) — prefer module-level cached functions or ",[19,2967,2453],{}," for per-instance caching.",[2836,2970,2971,2979,2980,488,2982,2984,2985,2988,2989,2992],{},[1502,2972,2973,1492,2975,2978],{},[19,2974,2258],{},[19,2976,2977],{},"cache"," require ALL arguments to be hashable"," — passing a ",[19,2981,1364],{},[19,2983,139],{},", or ",[19,2986,2987],{},"set"," argument raises ",[19,2990,2991],{},"TypeError: unhashable type",", which surprises developers who don't think of \"cacheability\" and \"hashability\" as the same constraint.",[2836,2994,2995,2868,3002,3005,3006,3009,3010,3012,3013,3016],{},[1502,2996,2997,1492,2999,3001],{},[19,2998,1272],{},[19,3000,1275],{}," return lazy iterators in Python 3 (unlike Python 2, where they returned lists)",[19,3003,3004],{},"map(f, data)"," must be wrapped in ",[19,3007,3008],{},"list(...)"," to materialize or iterated directly; passing the raw ",[19,3011,1272],{}," object where a list is expected (e.g., checking ",[19,3014,3015],{},"len()",") fails.",[2836,3018,3019,2868,3025,3027,3028,3031,3032,3035,3036,3038],{},[1502,3020,3021,3022,3024],{},"Closures inside ",[19,3023,21],{},"-wrapped or lambda-based loop bodies inherit the same late-binding trap as chapter 05's closures",[19,3026,2047],{}," bakes in argument ",[2951,3029,3030],{},"values"," at the time you call ",[19,3033,3034],{},"partial(...)"," (so it does NOT suffer late binding), but a ",[19,3037,1344],{}," created fresh inside a loop still does — don't confuse the two just because both \"capture\" something.",[14,3040,3042],{"id":3041},"spot-the-bug","🧠 Spot the Bug",[1487,3044,3045],{},"A report generator caches expensive computations per-report-object. Why does memory usage keep climbing in a long-running service that generates thousands of reports?",[28,3047,3048],{"language":30},[32,3049,3051],{"className":34,"code":3050,"language":30,"meta":36,"style":36},"from functools import lru_cache\n\nclass Report:\n    def __init__(self, report_id, rows):\n        self.report_id = report_id\n        self.rows = rows\n\n    @lru_cache(maxsize=128)\n    def total(self):\n        return sum(row[\"amount\"] for row in self.rows)\n\ndef generate_report(report_id, rows):\n    report = Report(report_id, rows)\n    return report.total()\n",[19,3052,3053,3063,3067,3075,3084,3096,3108,3112,3127,3136,3163,3167,3177,3187],{"__ignoreMap":36},[40,3054,3055,3057,3059,3061],{"class":42,"line":43},[40,3056,85],{"class":66},[40,3058,811],{"class":70},[40,3060,67],{"class":66},[40,3062,816],{"class":70},[40,3064,3065],{"class":42,"line":50},[40,3066,60],{"emptyLinePlaceholder":59},[40,3068,3069,3071,3073],{"class":42,"line":56},[40,3070,111],{"class":66},[40,3072,2366],{"class":104},[40,3074,117],{"class":70},[40,3076,3077,3079,3081],{"class":42,"line":63},[40,3078,841],{"class":66},[40,3080,844],{"class":123},[40,3082,3083],{"class":70},"(self, report_id, rows):\n",[40,3085,3086,3088,3091,3093],{"class":42,"line":74},[40,3087,857],{"class":123},[40,3089,3090],{"class":70},".report_id ",[40,3092,223],{"class":66},[40,3094,3095],{"class":70}," report_id\n",[40,3097,3098,3100,3103,3105],{"class":42,"line":82},[40,3099,857],{"class":123},[40,3101,3102],{"class":70},".rows ",[40,3104,223],{"class":66},[40,3106,3107],{"class":70}," rows\n",[40,3109,3110],{"class":42,"line":96},[40,3111,60],{"emptyLinePlaceholder":59},[40,3113,3114,3116,3118,3120,3122,3125],{"class":42,"line":101},[40,3115,874],{"class":104},[40,3117,877],{"class":70},[40,3119,880],{"class":709},[40,3121,223],{"class":66},[40,3123,3124],{"class":123},"128",[40,3126,992],{"class":70},[40,3128,3129,3131,3134],{"class":42,"line":108},[40,3130,841],{"class":66},[40,3132,3133],{"class":104}," total",[40,3135,2421],{"class":70},[40,3137,3138,3140,3142,3145,3148,3150,3152,3155,3157,3160],{"class":42,"line":120},[40,3139,926],{"class":66},[40,3141,2315],{"class":123},[40,3143,3144],{"class":70},"(row[",[40,3146,3147],{"class":193},"\"amount\"",[40,3149,287],{"class":70},[40,3151,229],{"class":66},[40,3153,3154],{"class":70}," row ",[40,3156,235],{"class":66},[40,3158,3159],{"class":123}," self",[40,3161,3162],{"class":70},".rows)\n",[40,3164,3165],{"class":42,"line":133},[40,3166,60],{"emptyLinePlaceholder":59},[40,3168,3169,3171,3174],{"class":42,"line":148},[40,3170,171],{"class":66},[40,3172,3173],{"class":104}," generate_report",[40,3175,3176],{"class":70},"(report_id, rows):\n",[40,3178,3179,3182,3184],{"class":42,"line":157},[40,3180,3181],{"class":70},"    report ",[40,3183,223],{"class":66},[40,3185,3186],{"class":70}," Report(report_id, rows)\n",[40,3188,3189,3191],{"class":42,"line":162},[40,3190,366],{"class":66},[40,3192,3193],{"class":70}," report.total()\n",[3195,3196,3197,3201,3252,3261],"details",{},[3198,3199,3200],"summary",{},"Answer",[1487,3202,3203,3204,3207,3208,3211,3212,3215,3216,3218,3219,3222,3223,3226,3227,3230,3231,3233,3234,3237,3238,3240,3241,3244,3245,3247,3248,3251],{},"Every call to ",[19,3205,3206],{},"generate_report"," creates a brand-new ",[19,3209,3210],{},"Report"," instance and calls ",[19,3213,3214],{},".total()"," on it exactly once — there's no realistic reuse to memoize. But ",[19,3217,1046],{}," on the ",[19,3220,3221],{},"total"," method caches by ",[19,3224,3225],{},"(self, )"," as the key, and the cache holds a ",[1502,3228,3229],{},"strong reference"," to each ",[19,3232,3210],{}," instance it has ever seen, up to ",[19,3235,3236],{},"maxsize=128"," entries — meaning up to 128 ",[19,3239,3210],{}," objects (and everything they transitively reference, including potentially large ",[19,3242,3243],{},"rows"," lists) are kept alive indefinitely, purely because they're pinned inside the cache's internal dict, long after ",[19,3246,3206],{}," has returned and any other reference to ",[19,3249,3250],{},"report"," has gone out of scope.",[1487,3253,3254,3255,3258,3259,2061],{},"The fix: don't cache instance methods that are called once per instance — either remove the cache entirely (there's no repeated call to memoize), or cache a free function keyed on hashable, meaningful arguments (e.g., ",[19,3256,3257],{},"report_id"," if totals are genuinely re-requested for the same id), not ",[19,3260,1185],{},[1487,3262,3263,127,3266,3268,3269,3271],{},[1502,3264,3265],{},"The lesson",[19,3267,1046],{}," caching ",[19,3270,1185],{}," as part of its key is only safe when instances are long-lived and genuinely reused across many calls — otherwise it silently converts a per-call cache into a memory leak, since the cache is what keeps those \"one-off\" instances from ever being garbage collected.",[14,3273,3275],{"id":3274},"key-takeaways","Key Takeaways",[2833,3277,3278,3281,3289,3302,3315],{},[2836,3279,3280],{},"Functions are first-class objects in Python — they can be stored, passed, and returned like any other value, which is the foundation for all functional-style code.",[2836,3282,3283,3285,3286,3288],{},[19,3284,1344],{}," is restricted to a single expression; reach for a named ",[19,3287,171],{}," the moment logic needs more than one line or a docstring.",[2836,3290,3291,3292,1492,3294,3296,3297,1492,3299,3301],{},"Prefer list\u002Fdict\u002Fset comprehensions over ",[19,3293,1272],{},[19,3295,1275],{}," with lambdas for readability — but ",[19,3298,1272],{},[19,3300,1275],{}," remain clean when passing an existing named function with no lambda needed.",[2836,3303,3304,3306,3307,1492,3309,3311,3312,3314],{},[19,3305,21],{}," pre-fills arguments; ",[19,3308,2067],{},[19,3310,2977],{}," memoizes pure functions — but never cache instance methods keyed on ",[19,3313,1185],{}," unless the instances are meant to be long-lived, or you'll create a silent memory leak.",[2836,3316,3317,3318,3321,3322,488,3324,3326],{},"Always apply ",[19,3319,3320],{},"@functools.wraps(func)"," inside a decorator's inner wrapper — otherwise you lose the wrapped function's ",[19,3323,2628],{},[19,3325,2642],{},", and introspectability.",[3328,3329,3330],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}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 pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}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);}",{"title":36,"searchDepth":50,"depth":50,"links":3332},[3333,3335,3337,3339,3341,3347,3349,3350,3351,3352,3353],{"id":16,"depth":50,"text":3334},"Production Pipeline — functools.partial + operator + Composition",{"id":1269,"depth":50,"text":3336},"map, filter, and Why Comprehensions Usually Win",{"id":1607,"depth":50,"text":3338},"functools.reduce — Fold a Sequence Into a Single Value",{"id":1865,"depth":50,"text":3340},"functools.partial — Pre-Filling Arguments",{"id":2064,"depth":50,"text":3342,"children":3343},"functools.lru_cache — Memoization in One Line",[3344,3346],{"id":2273,"depth":56,"text":3345},"The gotcha: lru_cache requires hashable arguments, and ignores default-vs-explicit distinctions loosely",{"id":2351,"depth":56,"text":2352},{"id":2457,"depth":50,"text":3348},"functools.wraps — Preserving Metadata Through Decorators",{"id":2675,"depth":50,"text":2676},{"id":2830,"depth":50,"text":2831},{"id":2929,"depth":50,"text":2930},{"id":3041,"depth":50,"text":3042},{"id":3274,"depth":50,"text":3275},"md",{},"\u002Fpython\u002F10-functional-programming",{"title":5,"description":36},"python\u002F10-functional-programming","1FkoKe2t68P64kVkki_Ao8TmDsLNdW4vHaT0J3yeHqI",1789924651485]