[{"data":1,"prerenderedAt":3989},["ShallowReactive",2],{"page-\u002Fpython\u002F26-performance-and-optimization":3},{"id":4,"title":5,"body":6,"description":27,"extension":3983,"meta":3984,"navigation":45,"path":3985,"seo":3986,"stem":3987,"__hash__":3988},"content\u002Fpython\u002F26-performance-and-optimization.md","26 — Performance & Optimization",{"type":7,"value":8,"toc":3965},"minimark",[9,13,18,645,1029,1429,1437,1510,1525,1696,1722,1729,1908,1932,2066,2070,2151,2194,2217,2246,2250,2399,2433,2613,2620,2812,2825,2910,2941,2948,3146,3166,3170,3317,3336,3484,3517,3521,3593,3597,3699,3703,3706,3790,3897,3901,3961],[10,11,5],"h1",{"id":12},"_26-performance-optimization",[14,15,17],"h2",{"id":16},"profiling-workflow-measure-before-optimizing","Profiling Workflow — Measure Before Optimizing",[19,20,22],"code-wrapper",{"language":21},"python",[23,24,28],"pre",{"className":25,"code":26,"language":21,"meta":27,"style":27},"language-python shiki shiki-themes github-light github-dark","# ── Production profiling workflow: cProfile → identify hotspots → line_profiler → optimize ──\n\nimport cProfile\nimport pstats\nimport time\nfrom io import StringIO\n\ndef find_primes_below(n: int) -> list[int]:\n    \"\"\"Sieve of Eratosthenes — a realistic CPU-bound function to profile.\"\"\"\n    sieve = [True] * n\n    sieve[0] = sieve[1] = False\n    for i in range(2, int(n ** 0.5) + 1):\n        if sieve[i]:\n            for j in range(i * i, n, i):\n                sieve[j] = False\n    return [i for i, is_prime in enumerate(sieve) if is_prime]\n\ndef count_primes_in_ranges(ranges: list[tuple[int, int]]) -> dict[int, int]:\n    \"\"\"Count primes in multiple ranges — calls find_primes_below repeatedly.\"\"\"\n    results = {}\n    for range_id, (lo, hi) in enumerate(ranges):\n        primes = find_primes_below(hi)\n        count = sum(1 for p in primes if p >= lo)\n        results[range_id] = count\n    return results\n\n# ── Step 1: cProfile — identify WHICH function dominates ──\ndef profile_with_cprofile():\n    \"\"\"Run cProfile and print top functions by cumulative time.\"\"\"\n    ranges = [(0, 100_000), (0, 200_000), (0, 50_000), (0, 300_000)]\n\n    profiler = cProfile.Profile()\n    profiler.enable()\n    result = count_primes_in_ranges(ranges)\n    profiler.disable()\n\n    # Sort by cumulative time — shows which functions dominate total runtime\n    stats = pstats.Stats(profiler)\n    stats.sort_stats(\"cumulative\")\n    stats.print_stats(10)   # top 10 functions\n\n    # Key columns:\n    #   ncalls    — how many times the function was called\n    #   tottime   — time IN this function alone (excluding sub-calls)\n    #   cumtime   — time including all sub-calls\n    #   percall   — tottime or cumtime \u002F ncalls\n\nprofile_with_cprofile()\n# Output reveals: find_primes_below dominates — called 4 times, most cumtime\n# The inner loop `for j in range(i*i, n, i): sieve[j] = False` is the actual hotspot\n","",[29,30,31,40,47,58,66,74,88,93,118,125,149,175,222,231,252,262,291,296,324,330,341,356,367,403,414,422,427,433,444,450,499,504,515,521,532,538,543,549,560,572,587,592,598,604,610,616,622,627,633,639],"code",{"__ignoreMap":27},[32,33,36],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"sdCPZ","# ── Production profiling workflow: cProfile → identify hotspots → line_profiler → optimize ──\n",[32,41,43],{"class":34,"line":42},2,[32,44,46],{"emptyLinePlaceholder":45},true,"\n",[32,48,50,54],{"class":34,"line":49},3,[32,51,53],{"class":52},"svdQ7","import",[32,55,57],{"class":56},"ssxIu"," cProfile\n",[32,59,61,63],{"class":34,"line":60},4,[32,62,53],{"class":52},[32,64,65],{"class":56}," pstats\n",[32,67,69,71],{"class":34,"line":68},5,[32,70,53],{"class":52},[32,72,73],{"class":56}," time\n",[32,75,77,80,83,85],{"class":34,"line":76},6,[32,78,79],{"class":52},"from",[32,81,82],{"class":56}," io ",[32,84,53],{"class":52},[32,86,87],{"class":56}," StringIO\n",[32,89,91],{"class":34,"line":90},7,[32,92,46],{"emptyLinePlaceholder":45},[32,94,96,99,103,106,110,113,115],{"class":34,"line":95},8,[32,97,98],{"class":52},"def",[32,100,102],{"class":101},"sIsaT"," find_primes_below",[32,104,105],{"class":56},"(n: ",[32,107,109],{"class":108},"snvgF","int",[32,111,112],{"class":56},") -> list[",[32,114,109],{"class":108},[32,116,117],{"class":56},"]:\n",[32,119,121],{"class":34,"line":120},9,[32,122,124],{"class":123},"sJ6F3","    \"\"\"Sieve of Eratosthenes — a realistic CPU-bound function to profile.\"\"\"\n",[32,126,128,131,134,137,140,143,146],{"class":34,"line":127},10,[32,129,130],{"class":56},"    sieve ",[32,132,133],{"class":52},"=",[32,135,136],{"class":56}," [",[32,138,139],{"class":108},"True",[32,141,142],{"class":56},"] ",[32,144,145],{"class":52},"*",[32,147,148],{"class":56}," n\n",[32,150,152,155,158,160,162,165,168,170,172],{"class":34,"line":151},11,[32,153,154],{"class":56},"    sieve[",[32,156,157],{"class":108},"0",[32,159,142],{"class":56},[32,161,133],{"class":52},[32,163,164],{"class":56}," sieve[",[32,166,167],{"class":108},"1",[32,169,142],{"class":56},[32,171,133],{"class":52},[32,173,174],{"class":108}," False\n",[32,176,178,181,184,187,190,193,196,199,201,204,207,210,213,216,219],{"class":34,"line":177},12,[32,179,180],{"class":52},"    for",[32,182,183],{"class":56}," i ",[32,185,186],{"class":52},"in",[32,188,189],{"class":108}," range",[32,191,192],{"class":56},"(",[32,194,195],{"class":108},"2",[32,197,198],{"class":56},", ",[32,200,109],{"class":108},[32,202,203],{"class":56},"(n ",[32,205,206],{"class":52},"**",[32,208,209],{"class":108}," 0.5",[32,211,212],{"class":56},") ",[32,214,215],{"class":52},"+",[32,217,218],{"class":108}," 1",[32,220,221],{"class":56},"):\n",[32,223,225,228],{"class":34,"line":224},13,[32,226,227],{"class":52},"        if",[32,229,230],{"class":56}," sieve[i]:\n",[32,232,234,237,240,242,244,247,249],{"class":34,"line":233},14,[32,235,236],{"class":52},"            for",[32,238,239],{"class":56}," j ",[32,241,186],{"class":52},[32,243,189],{"class":108},[32,245,246],{"class":56},"(i ",[32,248,145],{"class":52},[32,250,251],{"class":56}," i, n, i):\n",[32,253,255,258,260],{"class":34,"line":254},15,[32,256,257],{"class":56},"                sieve[j] ",[32,259,133],{"class":52},[32,261,174],{"class":108},[32,263,265,268,271,274,277,279,282,285,288],{"class":34,"line":264},16,[32,266,267],{"class":52},"    return",[32,269,270],{"class":56}," [i ",[32,272,273],{"class":52},"for",[32,275,276],{"class":56}," i, is_prime ",[32,278,186],{"class":52},[32,280,281],{"class":108}," enumerate",[32,283,284],{"class":56},"(sieve) ",[32,286,287],{"class":52},"if",[32,289,290],{"class":56}," is_prime]\n",[32,292,294],{"class":34,"line":293},17,[32,295,46],{"emptyLinePlaceholder":45},[32,297,299,301,304,307,309,311,313,316,318,320,322],{"class":34,"line":298},18,[32,300,98],{"class":52},[32,302,303],{"class":101}," count_primes_in_ranges",[32,305,306],{"class":56},"(ranges: list[tuple[",[32,308,109],{"class":108},[32,310,198],{"class":56},[32,312,109],{"class":108},[32,314,315],{"class":56},"]]) -> dict[",[32,317,109],{"class":108},[32,319,198],{"class":56},[32,321,109],{"class":108},[32,323,117],{"class":56},[32,325,327],{"class":34,"line":326},19,[32,328,329],{"class":123},"    \"\"\"Count primes in multiple ranges — calls find_primes_below repeatedly.\"\"\"\n",[32,331,333,336,338],{"class":34,"line":332},20,[32,334,335],{"class":56},"    results ",[32,337,133],{"class":52},[32,339,340],{"class":56}," {}\n",[32,342,344,346,349,351,353],{"class":34,"line":343},21,[32,345,180],{"class":52},[32,347,348],{"class":56}," range_id, (lo, hi) ",[32,350,186],{"class":52},[32,352,281],{"class":108},[32,354,355],{"class":56},"(ranges):\n",[32,357,359,362,364],{"class":34,"line":358},22,[32,360,361],{"class":56},"        primes ",[32,363,133],{"class":52},[32,365,366],{"class":56}," find_primes_below(hi)\n",[32,368,370,373,375,378,380,382,385,388,390,393,395,397,400],{"class":34,"line":369},23,[32,371,372],{"class":56},"        count ",[32,374,133],{"class":52},[32,376,377],{"class":108}," sum",[32,379,192],{"class":56},[32,381,167],{"class":108},[32,383,384],{"class":52}," for",[32,386,387],{"class":56}," p ",[32,389,186],{"class":52},[32,391,392],{"class":56}," primes ",[32,394,287],{"class":52},[32,396,387],{"class":56},[32,398,399],{"class":52},">=",[32,401,402],{"class":56}," lo)\n",[32,404,406,409,411],{"class":34,"line":405},24,[32,407,408],{"class":56},"        results[range_id] ",[32,410,133],{"class":52},[32,412,413],{"class":56}," count\n",[32,415,417,419],{"class":34,"line":416},25,[32,418,267],{"class":52},[32,420,421],{"class":56}," results\n",[32,423,425],{"class":34,"line":424},26,[32,426,46],{"emptyLinePlaceholder":45},[32,428,430],{"class":34,"line":429},27,[32,431,432],{"class":38},"# ── Step 1: cProfile — identify WHICH function dominates ──\n",[32,434,436,438,441],{"class":34,"line":435},28,[32,437,98],{"class":52},[32,439,440],{"class":101}," profile_with_cprofile",[32,442,443],{"class":56},"():\n",[32,445,447],{"class":34,"line":446},29,[32,448,449],{"class":123},"    \"\"\"Run cProfile and print top functions by cumulative time.\"\"\"\n",[32,451,453,456,458,461,463,465,468,471,473,475,478,480,482,484,487,489,491,493,496],{"class":34,"line":452},30,[32,454,455],{"class":56},"    ranges ",[32,457,133],{"class":52},[32,459,460],{"class":56}," [(",[32,462,157],{"class":108},[32,464,198],{"class":56},[32,466,467],{"class":108},"100_000",[32,469,470],{"class":56},"), (",[32,472,157],{"class":108},[32,474,198],{"class":56},[32,476,477],{"class":108},"200_000",[32,479,470],{"class":56},[32,481,157],{"class":108},[32,483,198],{"class":56},[32,485,486],{"class":108},"50_000",[32,488,470],{"class":56},[32,490,157],{"class":108},[32,492,198],{"class":56},[32,494,495],{"class":108},"300_000",[32,497,498],{"class":56},")]\n",[32,500,502],{"class":34,"line":501},31,[32,503,46],{"emptyLinePlaceholder":45},[32,505,507,510,512],{"class":34,"line":506},32,[32,508,509],{"class":56},"    profiler ",[32,511,133],{"class":52},[32,513,514],{"class":56}," cProfile.Profile()\n",[32,516,518],{"class":34,"line":517},33,[32,519,520],{"class":56},"    profiler.enable()\n",[32,522,524,527,529],{"class":34,"line":523},34,[32,525,526],{"class":56},"    result ",[32,528,133],{"class":52},[32,530,531],{"class":56}," count_primes_in_ranges(ranges)\n",[32,533,535],{"class":34,"line":534},35,[32,536,537],{"class":56},"    profiler.disable()\n",[32,539,541],{"class":34,"line":540},36,[32,542,46],{"emptyLinePlaceholder":45},[32,544,546],{"class":34,"line":545},37,[32,547,548],{"class":38},"    # Sort by cumulative time — shows which functions dominate total runtime\n",[32,550,552,555,557],{"class":34,"line":551},38,[32,553,554],{"class":56},"    stats ",[32,556,133],{"class":52},[32,558,559],{"class":56}," pstats.Stats(profiler)\n",[32,561,563,566,569],{"class":34,"line":562},39,[32,564,565],{"class":56},"    stats.sort_stats(",[32,567,568],{"class":123},"\"cumulative\"",[32,570,571],{"class":56},")\n",[32,573,575,578,581,584],{"class":34,"line":574},40,[32,576,577],{"class":56},"    stats.print_stats(",[32,579,580],{"class":108},"10",[32,582,583],{"class":56},")   ",[32,585,586],{"class":38},"# top 10 functions\n",[32,588,590],{"class":34,"line":589},41,[32,591,46],{"emptyLinePlaceholder":45},[32,593,595],{"class":34,"line":594},42,[32,596,597],{"class":38},"    # Key columns:\n",[32,599,601],{"class":34,"line":600},43,[32,602,603],{"class":38},"    #   ncalls    — how many times the function was called\n",[32,605,607],{"class":34,"line":606},44,[32,608,609],{"class":38},"    #   tottime   — time IN this function alone (excluding sub-calls)\n",[32,611,613],{"class":34,"line":612},45,[32,614,615],{"class":38},"    #   cumtime   — time including all sub-calls\n",[32,617,619],{"class":34,"line":618},46,[32,620,621],{"class":38},"    #   percall   — tottime or cumtime \u002F ncalls\n",[32,623,625],{"class":34,"line":624},47,[32,626,46],{"emptyLinePlaceholder":45},[32,628,630],{"class":34,"line":629},48,[32,631,632],{"class":56},"profile_with_cprofile()\n",[32,634,636],{"class":34,"line":635},49,[32,637,638],{"class":38},"# Output reveals: find_primes_below dominates — called 4 times, most cumtime\n",[32,640,642],{"class":34,"line":641},50,[32,643,644],{"class":38},"# The inner loop `for j in range(i*i, n, i): sieve[j] = False` is the actual hotspot\n",[19,646,647],{"language":21},[23,648,650],{"className":25,"code":649,"language":21,"meta":27,"style":27},"# ── Memory profiling with tracemalloc — find allocation hotspots ──\n\nimport tracemalloc\n\ndef memory_inefficient(n: int) -> list[int]:\n    \"\"\"Builds many intermediate lists — each comprehension allocates a new list.\"\"\"\n    result = []\n    for i in range(n):\n        squares = [x ** 2 for x in range(i)]   # allocates a new list EACH iteration\n        if squares:\n            result.append(squares[-1])\n    return result\n\ndef memory_efficient(n: int) -> list[int]:\n    \"\"\"Avoids intermediate lists — computes directly, O(1) extra memory per iteration.\"\"\"\n    return [(i - 1) ** 2 for i in range(1, n)]   # one list, no intermediates\n\ndef profile_memory(func, *args):\n    \"\"\"Snapshot tracemalloc before\u002Fafter, show top allocation sites.\"\"\"\n    tracemalloc.start()\n    snapshot_before = tracemalloc.take_snapshot()\n\n    func(*args)\n\n    snapshot_after = tracemalloc.take_snapshot()\n    stats = snapshot_after.compare_to(snapshot_before, \"lineno\")\n\n    print(f\"\\\\nMemory allocation diff for {func.__name__}:\")\n    for stat in stats[:5]:   # top 5 allocation sites\n        print(f\"  {stat}\")\n\nprofile_memory(memory_inefficient, 1000)\nprofile_memory(memory_efficient, 1000)\n# memory_inefficient shows many allocations from the inner comprehension line\n# memory_efficient shows a single allocation from the outer comprehension\n",[29,651,652,657,661,668,672,689,694,703,716,746,753,766,773,777,794,799,834,838,853,858,863,873,877,887,891,900,914,918,951,972,996,1000,1010,1019,1024],{"__ignoreMap":27},[32,653,654],{"class":34,"line":35},[32,655,656],{"class":38},"# ── Memory profiling with tracemalloc — find allocation hotspots ──\n",[32,658,659],{"class":34,"line":42},[32,660,46],{"emptyLinePlaceholder":45},[32,662,663,665],{"class":34,"line":49},[32,664,53],{"class":52},[32,666,667],{"class":56}," tracemalloc\n",[32,669,670],{"class":34,"line":60},[32,671,46],{"emptyLinePlaceholder":45},[32,673,674,676,679,681,683,685,687],{"class":34,"line":68},[32,675,98],{"class":52},[32,677,678],{"class":101}," memory_inefficient",[32,680,105],{"class":56},[32,682,109],{"class":108},[32,684,112],{"class":56},[32,686,109],{"class":108},[32,688,117],{"class":56},[32,690,691],{"class":34,"line":76},[32,692,693],{"class":123},"    \"\"\"Builds many intermediate lists — each comprehension allocates a new list.\"\"\"\n",[32,695,696,698,700],{"class":34,"line":90},[32,697,526],{"class":56},[32,699,133],{"class":52},[32,701,702],{"class":56}," []\n",[32,704,705,707,709,711,713],{"class":34,"line":95},[32,706,180],{"class":52},[32,708,183],{"class":56},[32,710,186],{"class":52},[32,712,189],{"class":108},[32,714,715],{"class":56},"(n):\n",[32,717,718,721,723,726,728,731,733,736,738,740,743],{"class":34,"line":120},[32,719,720],{"class":56},"        squares ",[32,722,133],{"class":52},[32,724,725],{"class":56}," [x ",[32,727,206],{"class":52},[32,729,730],{"class":108}," 2",[32,732,384],{"class":52},[32,734,735],{"class":56}," x ",[32,737,186],{"class":52},[32,739,189],{"class":108},[32,741,742],{"class":56},"(i)]   ",[32,744,745],{"class":38},"# allocates a new list EACH iteration\n",[32,747,748,750],{"class":34,"line":127},[32,749,227],{"class":52},[32,751,752],{"class":56}," squares:\n",[32,754,755,758,761,763],{"class":34,"line":151},[32,756,757],{"class":56},"            result.append(squares[",[32,759,760],{"class":52},"-",[32,762,167],{"class":108},[32,764,765],{"class":56},"])\n",[32,767,768,770],{"class":34,"line":177},[32,769,267],{"class":52},[32,771,772],{"class":56}," result\n",[32,774,775],{"class":34,"line":224},[32,776,46],{"emptyLinePlaceholder":45},[32,778,779,781,784,786,788,790,792],{"class":34,"line":233},[32,780,98],{"class":52},[32,782,783],{"class":101}," memory_efficient",[32,785,105],{"class":56},[32,787,109],{"class":108},[32,789,112],{"class":56},[32,791,109],{"class":108},[32,793,117],{"class":56},[32,795,796],{"class":34,"line":254},[32,797,798],{"class":123},"    \"\"\"Avoids intermediate lists — computes directly, O(1) extra memory per iteration.\"\"\"\n",[32,800,801,803,806,808,810,812,814,816,818,820,822,824,826,828,831],{"class":34,"line":264},[32,802,267],{"class":52},[32,804,805],{"class":56}," [(i ",[32,807,760],{"class":52},[32,809,218],{"class":108},[32,811,212],{"class":56},[32,813,206],{"class":52},[32,815,730],{"class":108},[32,817,384],{"class":52},[32,819,183],{"class":56},[32,821,186],{"class":52},[32,823,189],{"class":108},[32,825,192],{"class":56},[32,827,167],{"class":108},[32,829,830],{"class":56},", n)]   ",[32,832,833],{"class":38},"# one list, no intermediates\n",[32,835,836],{"class":34,"line":293},[32,837,46],{"emptyLinePlaceholder":45},[32,839,840,842,845,848,850],{"class":34,"line":298},[32,841,98],{"class":52},[32,843,844],{"class":101}," profile_memory",[32,846,847],{"class":56},"(func, ",[32,849,145],{"class":52},[32,851,852],{"class":56},"args):\n",[32,854,855],{"class":34,"line":326},[32,856,857],{"class":123},"    \"\"\"Snapshot tracemalloc before\u002Fafter, show top allocation sites.\"\"\"\n",[32,859,860],{"class":34,"line":332},[32,861,862],{"class":56},"    tracemalloc.start()\n",[32,864,865,868,870],{"class":34,"line":343},[32,866,867],{"class":56},"    snapshot_before ",[32,869,133],{"class":52},[32,871,872],{"class":56}," tracemalloc.take_snapshot()\n",[32,874,875],{"class":34,"line":358},[32,876,46],{"emptyLinePlaceholder":45},[32,878,879,882,884],{"class":34,"line":369},[32,880,881],{"class":56},"    func(",[32,883,145],{"class":52},[32,885,886],{"class":56},"args)\n",[32,888,889],{"class":34,"line":405},[32,890,46],{"emptyLinePlaceholder":45},[32,892,893,896,898],{"class":34,"line":416},[32,894,895],{"class":56},"    snapshot_after ",[32,897,133],{"class":52},[32,899,872],{"class":56},[32,901,902,904,906,909,912],{"class":34,"line":424},[32,903,554],{"class":56},[32,905,133],{"class":52},[32,907,908],{"class":56}," snapshot_after.compare_to(snapshot_before, ",[32,910,911],{"class":123},"\"lineno\"",[32,913,571],{"class":56},[32,915,916],{"class":34,"line":429},[32,917,46],{"emptyLinePlaceholder":45},[32,919,920,923,925,928,931,934,937,940,943,946,949],{"class":34,"line":435},[32,921,922],{"class":108},"    print",[32,924,192],{"class":56},[32,926,927],{"class":52},"f",[32,929,930],{"class":123},"\"",[32,932,933],{"class":108},"\\\\",[32,935,936],{"class":123},"nMemory allocation diff for ",[32,938,939],{"class":108},"{",[32,941,942],{"class":56},"func.",[32,944,945],{"class":108},"__name__}",[32,947,948],{"class":123},":\"",[32,950,571],{"class":56},[32,952,953,955,958,960,963,966,969],{"class":34,"line":446},[32,954,180],{"class":52},[32,956,957],{"class":56}," stat ",[32,959,186],{"class":52},[32,961,962],{"class":56}," stats[:",[32,964,965],{"class":108},"5",[32,967,968],{"class":56},"]:   ",[32,970,971],{"class":38},"# top 5 allocation sites\n",[32,973,974,977,979,981,984,986,989,992,994],{"class":34,"line":452},[32,975,976],{"class":108},"        print",[32,978,192],{"class":56},[32,980,927],{"class":52},[32,982,983],{"class":123},"\"  ",[32,985,939],{"class":108},[32,987,988],{"class":56},"stat",[32,990,991],{"class":108},"}",[32,993,930],{"class":123},[32,995,571],{"class":56},[32,997,998],{"class":34,"line":501},[32,999,46],{"emptyLinePlaceholder":45},[32,1001,1002,1005,1008],{"class":34,"line":506},[32,1003,1004],{"class":56},"profile_memory(memory_inefficient, ",[32,1006,1007],{"class":108},"1000",[32,1009,571],{"class":56},[32,1011,1012,1015,1017],{"class":34,"line":517},[32,1013,1014],{"class":56},"profile_memory(memory_efficient, ",[32,1016,1007],{"class":108},[32,1018,571],{"class":56},[32,1020,1021],{"class":34,"line":523},[32,1022,1023],{"class":38},"# memory_inefficient shows many allocations from the inner comprehension line\n",[32,1025,1026],{"class":34,"line":534},[32,1027,1028],{"class":38},"# memory_efficient shows a single allocation from the outer comprehension\n",[19,1030,1031],{"language":21},[23,1032,1034],{"className":25,"code":1033,"language":21,"meta":27,"style":27},"# ── numpy vectorization: the single biggest performance win in numeric Python ──\n\nimport numpy as np\nimport time\n\nN = 10_000_000\n\n# ANTI-PATTERN: element-wise Python loop over numpy array — defeats vectorization\ndef slow_square(arr: np.ndarray) -> np.ndarray:\n    result = np.empty_like(arr)\n    for i in range(len(arr)):\n        result[i] = arr[i] ** 2   # Python loop, per-element dispatch\n    return result\n\n# CORRECT: vectorized operation — single C call over contiguous memory\ndef fast_square(arr: np.ndarray) -> np.ndarray:\n    return arr ** 2   # numpy dispatches to SIMD-optimized C, no Python loop\n\narr = np.arange(N, dtype=np.float64)\n\nt0 = time.perf_counter()\n_ = fast_square(arr)\nvectorized = time.perf_counter() - t0\nprint(f\"numpy vectorized: {vectorized:.4f}s  ({N:,} elements, SIMD C)\")\n\n# Don't even try the slow version on 10M elements — it would take minutes\n# On 100k elements for comparison:\nsmall = np.arange(100_000, dtype=np.float64)\nt0 = time.perf_counter()\n_ = slow_square(small)\npython_loop = time.perf_counter() - t0\nt0 = time.perf_counter()\n_ = fast_square(small)\nnumpy_fast = time.perf_counter() - t0\nprint(f\"Python loop (100k): {python_loop:.4f}s vs numpy: {numpy_fast:.4f}s → {python_loop\u002Fnumpy_fast:.0f}x faster\")\n",[29,1035,1036,1041,1045,1058,1064,1068,1078,1082,1087,1097,1106,1124,1141,1147,1151,1156,1165,1179,1183,1202,1206,1216,1226,1241,1281,1285,1290,1295,1315,1323,1332,1345,1353,1362,1375],{"__ignoreMap":27},[32,1037,1038],{"class":34,"line":35},[32,1039,1040],{"class":38},"# ── numpy vectorization: the single biggest performance win in numeric Python ──\n",[32,1042,1043],{"class":34,"line":42},[32,1044,46],{"emptyLinePlaceholder":45},[32,1046,1047,1049,1052,1055],{"class":34,"line":49},[32,1048,53],{"class":52},[32,1050,1051],{"class":56}," numpy ",[32,1053,1054],{"class":52},"as",[32,1056,1057],{"class":56}," np\n",[32,1059,1060,1062],{"class":34,"line":60},[32,1061,53],{"class":52},[32,1063,73],{"class":56},[32,1065,1066],{"class":34,"line":68},[32,1067,46],{"emptyLinePlaceholder":45},[32,1069,1070,1073,1075],{"class":34,"line":76},[32,1071,1072],{"class":56},"N ",[32,1074,133],{"class":52},[32,1076,1077],{"class":108}," 10_000_000\n",[32,1079,1080],{"class":34,"line":90},[32,1081,46],{"emptyLinePlaceholder":45},[32,1083,1084],{"class":34,"line":95},[32,1085,1086],{"class":38},"# ANTI-PATTERN: element-wise Python loop over numpy array — defeats vectorization\n",[32,1088,1089,1091,1094],{"class":34,"line":120},[32,1090,98],{"class":52},[32,1092,1093],{"class":101}," slow_square",[32,1095,1096],{"class":56},"(arr: np.ndarray) -> np.ndarray:\n",[32,1098,1099,1101,1103],{"class":34,"line":127},[32,1100,526],{"class":56},[32,1102,133],{"class":52},[32,1104,1105],{"class":56}," np.empty_like(arr)\n",[32,1107,1108,1110,1112,1114,1116,1118,1121],{"class":34,"line":151},[32,1109,180],{"class":52},[32,1111,183],{"class":56},[32,1113,186],{"class":52},[32,1115,189],{"class":108},[32,1117,192],{"class":56},[32,1119,1120],{"class":108},"len",[32,1122,1123],{"class":56},"(arr)):\n",[32,1125,1126,1129,1131,1134,1136,1138],{"class":34,"line":177},[32,1127,1128],{"class":56},"        result[i] ",[32,1130,133],{"class":52},[32,1132,1133],{"class":56}," arr[i] ",[32,1135,206],{"class":52},[32,1137,730],{"class":108},[32,1139,1140],{"class":38},"   # Python loop, per-element dispatch\n",[32,1142,1143,1145],{"class":34,"line":224},[32,1144,267],{"class":52},[32,1146,772],{"class":56},[32,1148,1149],{"class":34,"line":233},[32,1150,46],{"emptyLinePlaceholder":45},[32,1152,1153],{"class":34,"line":254},[32,1154,1155],{"class":38},"# CORRECT: vectorized operation — single C call over contiguous memory\n",[32,1157,1158,1160,1163],{"class":34,"line":264},[32,1159,98],{"class":52},[32,1161,1162],{"class":101}," fast_square",[32,1164,1096],{"class":56},[32,1166,1167,1169,1172,1174,1176],{"class":34,"line":293},[32,1168,267],{"class":52},[32,1170,1171],{"class":56}," arr ",[32,1173,206],{"class":52},[32,1175,730],{"class":108},[32,1177,1178],{"class":38},"   # numpy dispatches to SIMD-optimized C, no Python loop\n",[32,1180,1181],{"class":34,"line":298},[32,1182,46],{"emptyLinePlaceholder":45},[32,1184,1185,1188,1190,1193,1197,1199],{"class":34,"line":326},[32,1186,1187],{"class":56},"arr ",[32,1189,133],{"class":52},[32,1191,1192],{"class":56}," np.arange(N, ",[32,1194,1196],{"class":1195},"sCrzJ","dtype",[32,1198,133],{"class":52},[32,1200,1201],{"class":56},"np.float64)\n",[32,1203,1204],{"class":34,"line":332},[32,1205,46],{"emptyLinePlaceholder":45},[32,1207,1208,1211,1213],{"class":34,"line":343},[32,1209,1210],{"class":56},"t0 ",[32,1212,133],{"class":52},[32,1214,1215],{"class":56}," time.perf_counter()\n",[32,1217,1218,1221,1223],{"class":34,"line":358},[32,1219,1220],{"class":56},"_ ",[32,1222,133],{"class":52},[32,1224,1225],{"class":56}," fast_square(arr)\n",[32,1227,1228,1231,1233,1236,1238],{"class":34,"line":369},[32,1229,1230],{"class":56},"vectorized ",[32,1232,133],{"class":52},[32,1234,1235],{"class":56}," time.perf_counter() ",[32,1237,760],{"class":52},[32,1239,1240],{"class":56}," t0\n",[32,1242,1243,1246,1248,1250,1253,1255,1258,1261,1263,1266,1268,1271,1274,1276,1279],{"class":34,"line":405},[32,1244,1245],{"class":108},"print",[32,1247,192],{"class":56},[32,1249,927],{"class":52},[32,1251,1252],{"class":123},"\"numpy vectorized: ",[32,1254,939],{"class":108},[32,1256,1257],{"class":56},"vectorized",[32,1259,1260],{"class":52},":.4f",[32,1262,991],{"class":108},[32,1264,1265],{"class":123},"s  (",[32,1267,939],{"class":108},[32,1269,1270],{"class":56},"N",[32,1272,1273],{"class":52},":,",[32,1275,991],{"class":108},[32,1277,1278],{"class":123}," elements, SIMD C)\"",[32,1280,571],{"class":56},[32,1282,1283],{"class":34,"line":416},[32,1284,46],{"emptyLinePlaceholder":45},[32,1286,1287],{"class":34,"line":424},[32,1288,1289],{"class":38},"# Don't even try the slow version on 10M elements — it would take minutes\n",[32,1291,1292],{"class":34,"line":429},[32,1293,1294],{"class":38},"# On 100k elements for comparison:\n",[32,1296,1297,1300,1302,1305,1307,1309,1311,1313],{"class":34,"line":435},[32,1298,1299],{"class":56},"small ",[32,1301,133],{"class":52},[32,1303,1304],{"class":56}," np.arange(",[32,1306,467],{"class":108},[32,1308,198],{"class":56},[32,1310,1196],{"class":1195},[32,1312,133],{"class":52},[32,1314,1201],{"class":56},[32,1316,1317,1319,1321],{"class":34,"line":446},[32,1318,1210],{"class":56},[32,1320,133],{"class":52},[32,1322,1215],{"class":56},[32,1324,1325,1327,1329],{"class":34,"line":452},[32,1326,1220],{"class":56},[32,1328,133],{"class":52},[32,1330,1331],{"class":56}," slow_square(small)\n",[32,1333,1334,1337,1339,1341,1343],{"class":34,"line":501},[32,1335,1336],{"class":56},"python_loop ",[32,1338,133],{"class":52},[32,1340,1235],{"class":56},[32,1342,760],{"class":52},[32,1344,1240],{"class":56},[32,1346,1347,1349,1351],{"class":34,"line":506},[32,1348,1210],{"class":56},[32,1350,133],{"class":52},[32,1352,1215],{"class":56},[32,1354,1355,1357,1359],{"class":34,"line":517},[32,1356,1220],{"class":56},[32,1358,133],{"class":52},[32,1360,1361],{"class":56}," fast_square(small)\n",[32,1363,1364,1367,1369,1371,1373],{"class":34,"line":523},[32,1365,1366],{"class":56},"numpy_fast ",[32,1368,133],{"class":52},[32,1370,1235],{"class":56},[32,1372,760],{"class":52},[32,1374,1240],{"class":56},[32,1376,1377,1379,1381,1383,1386,1388,1391,1393,1395,1398,1400,1403,1405,1407,1410,1412,1414,1417,1419,1422,1424,1427],{"class":34,"line":534},[32,1378,1245],{"class":108},[32,1380,192],{"class":56},[32,1382,927],{"class":52},[32,1384,1385],{"class":123},"\"Python loop (100k): ",[32,1387,939],{"class":108},[32,1389,1390],{"class":56},"python_loop",[32,1392,1260],{"class":52},[32,1394,991],{"class":108},[32,1396,1397],{"class":123},"s vs numpy: ",[32,1399,939],{"class":108},[32,1401,1402],{"class":56},"numpy_fast",[32,1404,1260],{"class":52},[32,1406,991],{"class":108},[32,1408,1409],{"class":123},"s → ",[32,1411,939],{"class":108},[32,1413,1390],{"class":56},[32,1415,1416],{"class":52},"\u002F",[32,1418,1402],{"class":56},[32,1420,1421],{"class":52},":.0f",[32,1423,991],{"class":108},[32,1425,1426],{"class":123},"x faster\"",[32,1428,571],{"class":56},[14,1430,1432,1433,1436],{"id":1431},"the-gil-dis-and-why-python-loops-are-slow","The GIL, ",[29,1434,1435],{},"dis",", and Why Python Loops Are Slow",[19,1438,1439],{"language":21},[23,1440,1442],{"className":25,"code":1441,"language":21,"meta":27,"style":27},"import dis\n\ndef add_one(x):\n    return x + 1\n\ndis.dis(add_one)\n#   2           0 RESUME                   0\n#               2 LOAD_FAST                0 (x)\n#               4 LOAD_CONST               1 (1)\n#               6 BINARY_OP                0 (+)\n#              10 RETURN_VALUE\n",[29,1443,1444,1451,1455,1465,1476,1480,1485,1490,1495,1500,1505],{"__ignoreMap":27},[32,1445,1446,1448],{"class":34,"line":35},[32,1447,53],{"class":52},[32,1449,1450],{"class":56}," dis\n",[32,1452,1453],{"class":34,"line":42},[32,1454,46],{"emptyLinePlaceholder":45},[32,1456,1457,1459,1462],{"class":34,"line":49},[32,1458,98],{"class":52},[32,1460,1461],{"class":101}," add_one",[32,1463,1464],{"class":56},"(x):\n",[32,1466,1467,1469,1471,1473],{"class":34,"line":60},[32,1468,267],{"class":52},[32,1470,735],{"class":56},[32,1472,215],{"class":52},[32,1474,1475],{"class":108}," 1\n",[32,1477,1478],{"class":34,"line":68},[32,1479,46],{"emptyLinePlaceholder":45},[32,1481,1482],{"class":34,"line":76},[32,1483,1484],{"class":56},"dis.dis(add_one)\n",[32,1486,1487],{"class":34,"line":90},[32,1488,1489],{"class":38},"#   2           0 RESUME                   0\n",[32,1491,1492],{"class":34,"line":95},[32,1493,1494],{"class":38},"#               2 LOAD_FAST                0 (x)\n",[32,1496,1497],{"class":34,"line":120},[32,1498,1499],{"class":38},"#               4 LOAD_CONST               1 (1)\n",[32,1501,1502],{"class":34,"line":127},[32,1503,1504],{"class":38},"#               6 BINARY_OP                0 (+)\n",[32,1506,1507],{"class":34,"line":151},[32,1508,1509],{"class":38},"#              10 RETURN_VALUE\n",[1511,1512,1513,1514,1517,1518,1521,1522,1524],"p",{},"Every Python-level operation — even ",[29,1515,1516],{},"x + 1"," — compiles to multiple bytecode instructions, each dispatched through the interpreter's evaluation loop, each involving type checks and dynamic dispatch (",[29,1519,1520],{},"__add__"," lookup, etc.) that a compiled language resolves once at compile time. This is the fundamental reason a pure-Python ",[29,1523,273],{}," loop summing a million numbers is orders of magnitude slower than the equivalent C loop: not one slow operation, but millions of small dynamic-dispatch overheads compounding.",[19,1526,1527],{"language":21},[23,1528,1530],{"className":25,"code":1529,"language":21,"meta":27,"style":27},"import time\n\ndata = list(range(10_000_000))\n\nstart = time.perf_counter()\ntotal = 0\nfor x in data:\n    total += x\nprint(f\"pure loop: {time.perf_counter() - start:.4f}s\")\n\nstart = time.perf_counter()\ntotal = sum(data)             # sum() runs its loop in C, not in the bytecode interpreter\nprint(f\"builtin sum(): {time.perf_counter() - start:.4f}s\")   # typically 5-10x faster\n",[29,1531,1532,1538,1542,1565,1569,1578,1588,1599,1610,1640,1644,1652,1666],{"__ignoreMap":27},[32,1533,1534,1536],{"class":34,"line":35},[32,1535,53],{"class":52},[32,1537,73],{"class":56},[32,1539,1540],{"class":34,"line":42},[32,1541,46],{"emptyLinePlaceholder":45},[32,1543,1544,1547,1549,1552,1554,1557,1559,1562],{"class":34,"line":49},[32,1545,1546],{"class":56},"data ",[32,1548,133],{"class":52},[32,1550,1551],{"class":108}," list",[32,1553,192],{"class":56},[32,1555,1556],{"class":108},"range",[32,1558,192],{"class":56},[32,1560,1561],{"class":108},"10_000_000",[32,1563,1564],{"class":56},"))\n",[32,1566,1567],{"class":34,"line":60},[32,1568,46],{"emptyLinePlaceholder":45},[32,1570,1571,1574,1576],{"class":34,"line":68},[32,1572,1573],{"class":56},"start ",[32,1575,133],{"class":52},[32,1577,1215],{"class":56},[32,1579,1580,1583,1585],{"class":34,"line":76},[32,1581,1582],{"class":56},"total ",[32,1584,133],{"class":52},[32,1586,1587],{"class":108}," 0\n",[32,1589,1590,1592,1594,1596],{"class":34,"line":90},[32,1591,273],{"class":52},[32,1593,735],{"class":56},[32,1595,186],{"class":52},[32,1597,1598],{"class":56}," data:\n",[32,1600,1601,1604,1607],{"class":34,"line":95},[32,1602,1603],{"class":56},"    total ",[32,1605,1606],{"class":52},"+=",[32,1608,1609],{"class":56}," x\n",[32,1611,1612,1614,1616,1618,1621,1623,1626,1628,1631,1633,1635,1638],{"class":34,"line":120},[32,1613,1245],{"class":108},[32,1615,192],{"class":56},[32,1617,927],{"class":52},[32,1619,1620],{"class":123},"\"pure loop: ",[32,1622,939],{"class":108},[32,1624,1625],{"class":56},"time.perf_counter() ",[32,1627,760],{"class":52},[32,1629,1630],{"class":56}," start",[32,1632,1260],{"class":52},[32,1634,991],{"class":108},[32,1636,1637],{"class":123},"s\"",[32,1639,571],{"class":56},[32,1641,1642],{"class":34,"line":127},[32,1643,46],{"emptyLinePlaceholder":45},[32,1645,1646,1648,1650],{"class":34,"line":151},[32,1647,1573],{"class":56},[32,1649,133],{"class":52},[32,1651,1215],{"class":56},[32,1653,1654,1656,1658,1660,1663],{"class":34,"line":177},[32,1655,1582],{"class":56},[32,1657,133],{"class":52},[32,1659,377],{"class":108},[32,1661,1662],{"class":56},"(data)             ",[32,1664,1665],{"class":38},"# sum() runs its loop in C, not in the bytecode interpreter\n",[32,1667,1668,1670,1672,1674,1677,1679,1681,1683,1685,1687,1689,1691,1693],{"class":34,"line":224},[32,1669,1245],{"class":108},[32,1671,192],{"class":56},[32,1673,927],{"class":52},[32,1675,1676],{"class":123},"\"builtin sum(): ",[32,1678,939],{"class":108},[32,1680,1625],{"class":56},[32,1682,760],{"class":52},[32,1684,1630],{"class":56},[32,1686,1260],{"class":52},[32,1688,991],{"class":108},[32,1690,1637],{"class":123},[32,1692,583],{"class":56},[32,1694,1695],{"class":38},"# typically 5-10x faster\n",[1511,1697,1698,1702,1703,198,1706,198,1709,198,1712,198,1715,198,1718,1721],{},[1699,1700,1701],"strong",{},"Best practice",": prefer built-ins (",[29,1704,1705],{},"sum",[29,1707,1708],{},"min",[29,1710,1711],{},"max",[29,1713,1714],{},"sorted",[29,1716,1717],{},"any",[29,1719,1720],{},"all",") and comprehensions over hand-written Python loops for anything performance-sensitive — they execute their iteration in C, sidestepping per-iteration bytecode dispatch overhead entirely, often for a 5-10x speedup with no algorithmic change at all.",[14,1723,1725,1728],{"id":1724},"numpy-vectorization-over-loops",[29,1726,1727],{},"numpy",": Vectorization Over Loops",[19,1730,1731],{"language":21},[23,1732,1734],{"className":25,"code":1733,"language":21,"meta":27,"style":27},"import numpy as np\nimport time\n\nsize = 10_000_000\npython_list = list(range(size))\nnumpy_array = np.arange(size)\n\nstart = time.perf_counter()\nsquared_list = [x * x for x in python_list]\nprint(f\"list comprehension: {time.perf_counter() - start:.4f}s\")\n\nstart = time.perf_counter()\nsquared_array = numpy_array ** 2\nprint(f\"numpy vectorized: {time.perf_counter() - start:.4f}s\")   # typically 20-50x faster\n",[29,1735,1736,1746,1752,1756,1765,1781,1791,1795,1803,1825,1852,1856,1864,1879],{"__ignoreMap":27},[32,1737,1738,1740,1742,1744],{"class":34,"line":35},[32,1739,53],{"class":52},[32,1741,1051],{"class":56},[32,1743,1054],{"class":52},[32,1745,1057],{"class":56},[32,1747,1748,1750],{"class":34,"line":42},[32,1749,53],{"class":52},[32,1751,73],{"class":56},[32,1753,1754],{"class":34,"line":49},[32,1755,46],{"emptyLinePlaceholder":45},[32,1757,1758,1761,1763],{"class":34,"line":60},[32,1759,1760],{"class":56},"size ",[32,1762,133],{"class":52},[32,1764,1077],{"class":108},[32,1766,1767,1770,1772,1774,1776,1778],{"class":34,"line":68},[32,1768,1769],{"class":56},"python_list ",[32,1771,133],{"class":52},[32,1773,1551],{"class":108},[32,1775,192],{"class":56},[32,1777,1556],{"class":108},[32,1779,1780],{"class":56},"(size))\n",[32,1782,1783,1786,1788],{"class":34,"line":76},[32,1784,1785],{"class":56},"numpy_array ",[32,1787,133],{"class":52},[32,1789,1790],{"class":56}," np.arange(size)\n",[32,1792,1793],{"class":34,"line":90},[32,1794,46],{"emptyLinePlaceholder":45},[32,1796,1797,1799,1801],{"class":34,"line":95},[32,1798,1573],{"class":56},[32,1800,133],{"class":52},[32,1802,1215],{"class":56},[32,1804,1805,1808,1810,1812,1814,1816,1818,1820,1822],{"class":34,"line":120},[32,1806,1807],{"class":56},"squared_list ",[32,1809,133],{"class":52},[32,1811,725],{"class":56},[32,1813,145],{"class":52},[32,1815,735],{"class":56},[32,1817,273],{"class":52},[32,1819,735],{"class":56},[32,1821,186],{"class":52},[32,1823,1824],{"class":56}," python_list]\n",[32,1826,1827,1829,1831,1833,1836,1838,1840,1842,1844,1846,1848,1850],{"class":34,"line":127},[32,1828,1245],{"class":108},[32,1830,192],{"class":56},[32,1832,927],{"class":52},[32,1834,1835],{"class":123},"\"list comprehension: ",[32,1837,939],{"class":108},[32,1839,1625],{"class":56},[32,1841,760],{"class":52},[32,1843,1630],{"class":56},[32,1845,1260],{"class":52},[32,1847,991],{"class":108},[32,1849,1637],{"class":123},[32,1851,571],{"class":56},[32,1853,1854],{"class":34,"line":151},[32,1855,46],{"emptyLinePlaceholder":45},[32,1857,1858,1860,1862],{"class":34,"line":177},[32,1859,1573],{"class":56},[32,1861,133],{"class":52},[32,1863,1215],{"class":56},[32,1865,1866,1869,1871,1874,1876],{"class":34,"line":224},[32,1867,1868],{"class":56},"squared_array ",[32,1870,133],{"class":52},[32,1872,1873],{"class":56}," numpy_array ",[32,1875,206],{"class":52},[32,1877,1878],{"class":108}," 2\n",[32,1880,1881,1883,1885,1887,1889,1891,1893,1895,1897,1899,1901,1903,1905],{"class":34,"line":233},[32,1882,1245],{"class":108},[32,1884,192],{"class":56},[32,1886,927],{"class":52},[32,1888,1252],{"class":123},[32,1890,939],{"class":108},[32,1892,1625],{"class":56},[32,1894,760],{"class":52},[32,1896,1630],{"class":56},[32,1898,1260],{"class":52},[32,1900,991],{"class":108},[32,1902,1637],{"class":123},[32,1904,583],{"class":56},[32,1906,1907],{"class":38},"# typically 20-50x faster\n",[1511,1909,1910,1912,1913,198,1915,1917,1918,1921,1922,1924,1925,1927,1928,1931],{},[29,1911,1727],{}," arrays are backed by contiguous, fixed-type C memory blocks, and operations like ",[29,1914,206],{},[29,1916,215],{},", or ",[29,1919,1920],{},"np.sum()"," dispatch to compiled C (or SIMD-vectorized) loops operating on that raw memory directly — no per-element Python object boxing, no per-element type dispatch. ",[1699,1923,1701],{},": any numerical workload processing more than a few thousand elements — matrix math, signal processing, statistics, image data — belongs in ",[29,1926,1727],{}," (or ",[29,1929,1930],{},"pandas",", built on it), not a Python list with manual loops; this single change routinely accounts for the largest performance win available in data-heavy code.",[19,1933,1934],{"language":21},[23,1935,1937],{"className":25,"code":1936,"language":21,"meta":27,"style":27},"prices = np.array([19.99, 5.50, 100.00, 0.99])\nquantities = np.array([2, 10, 1, 50])\n\n# WRONG mindset (works, but throws away numpy's advantage):\ntotal = 0\nfor i in range(len(prices)):\n    total += prices[i] * quantities[i]\n\n# RIGHT — vectorized, no Python-level loop at all:\ntotal = (prices * quantities).sum()\n",[29,1938,1939,1969,1995,1999,2004,2012,2029,2043,2047,2052],{"__ignoreMap":27},[32,1940,1941,1944,1946,1949,1952,1954,1957,1959,1962,1964,1967],{"class":34,"line":35},[32,1942,1943],{"class":56},"prices ",[32,1945,133],{"class":52},[32,1947,1948],{"class":56}," np.array([",[32,1950,1951],{"class":108},"19.99",[32,1953,198],{"class":56},[32,1955,1956],{"class":108},"5.50",[32,1958,198],{"class":56},[32,1960,1961],{"class":108},"100.00",[32,1963,198],{"class":56},[32,1965,1966],{"class":108},"0.99",[32,1968,765],{"class":56},[32,1970,1971,1974,1976,1978,1980,1982,1984,1986,1988,1990,1993],{"class":34,"line":42},[32,1972,1973],{"class":56},"quantities ",[32,1975,133],{"class":52},[32,1977,1948],{"class":56},[32,1979,195],{"class":108},[32,1981,198],{"class":56},[32,1983,580],{"class":108},[32,1985,198],{"class":56},[32,1987,167],{"class":108},[32,1989,198],{"class":56},[32,1991,1992],{"class":108},"50",[32,1994,765],{"class":56},[32,1996,1997],{"class":34,"line":49},[32,1998,46],{"emptyLinePlaceholder":45},[32,2000,2001],{"class":34,"line":60},[32,2002,2003],{"class":38},"# WRONG mindset (works, but throws away numpy's advantage):\n",[32,2005,2006,2008,2010],{"class":34,"line":68},[32,2007,1582],{"class":56},[32,2009,133],{"class":52},[32,2011,1587],{"class":108},[32,2013,2014,2016,2018,2020,2022,2024,2026],{"class":34,"line":76},[32,2015,273],{"class":52},[32,2017,183],{"class":56},[32,2019,186],{"class":52},[32,2021,189],{"class":108},[32,2023,192],{"class":56},[32,2025,1120],{"class":108},[32,2027,2028],{"class":56},"(prices)):\n",[32,2030,2031,2033,2035,2038,2040],{"class":34,"line":90},[32,2032,1603],{"class":56},[32,2034,1606],{"class":52},[32,2036,2037],{"class":56}," prices[i] ",[32,2039,145],{"class":52},[32,2041,2042],{"class":56}," quantities[i]\n",[32,2044,2045],{"class":34,"line":95},[32,2046,46],{"emptyLinePlaceholder":45},[32,2048,2049],{"class":34,"line":120},[32,2050,2051],{"class":38},"# RIGHT — vectorized, no Python-level loop at all:\n",[32,2053,2054,2056,2058,2061,2063],{"class":34,"line":127},[32,2055,1582],{"class":56},[32,2057,133],{"class":52},[32,2059,2060],{"class":56}," (prices ",[32,2062,145],{"class":52},[32,2064,2065],{"class":56}," quantities).sum()\n",[14,2067,2069],{"id":2068},"cython-and-c-extensions-when-pure-python-isnt-enough","Cython and C Extensions: When Pure Python Isn't Enough",[19,2071,2072],{"language":21},[23,2073,2075],{"className":25,"code":2074,"language":21,"meta":27,"style":27},"# fib.pyx — Cython source, a superset of Python with optional static typing\ndef fib_cython(int n):\n    cdef int a = 0, b = 1, i\n    for i in range(n):\n        a, b = b, a + b\n    return a\n",[29,2076,2077,2082,2092,2117,2129,2144],{"__ignoreMap":27},[32,2078,2079],{"class":34,"line":35},[32,2080,2081],{"class":38},"# fib.pyx — Cython source, a superset of Python with optional static typing\n",[32,2083,2084,2086,2089],{"class":34,"line":42},[32,2085,98],{"class":52},[32,2087,2088],{"class":101}," fib_cython",[32,2090,2091],{"class":56},"(int n):\n",[32,2093,2094,2097,2099,2102,2104,2107,2110,2112,2114],{"class":34,"line":49},[32,2095,2096],{"class":56},"    cdef ",[32,2098,109],{"class":108},[32,2100,2101],{"class":56}," a ",[32,2103,133],{"class":52},[32,2105,2106],{"class":108}," 0",[32,2108,2109],{"class":56},", b ",[32,2111,133],{"class":52},[32,2113,218],{"class":108},[32,2115,2116],{"class":56},", i\n",[32,2118,2119,2121,2123,2125,2127],{"class":34,"line":60},[32,2120,180],{"class":52},[32,2122,183],{"class":56},[32,2124,186],{"class":52},[32,2126,189],{"class":108},[32,2128,715],{"class":56},[32,2130,2131,2134,2136,2139,2141],{"class":34,"line":68},[32,2132,2133],{"class":56},"        a, b ",[32,2135,133],{"class":52},[32,2137,2138],{"class":56}," b, a ",[32,2140,215],{"class":52},[32,2142,2143],{"class":56}," b\n",[32,2145,2146,2148],{"class":34,"line":76},[32,2147,267],{"class":52},[32,2149,2150],{"class":56}," a\n",[19,2152,2154],{"language":2153},"toml",[23,2155,2158],{"className":2156,"code":2157,"language":2153,"meta":27,"style":27},"language-toml shiki shiki-themes github-light github-dark","[build-system]\nrequires = [\"setuptools\", \"Cython\"]\nbuild-backend = \"setuptools.build_meta\"\n",[29,2159,2160,2171,2186],{"__ignoreMap":27},[32,2161,2162,2165,2168],{"class":34,"line":35},[32,2163,2164],{"class":56},"[",[32,2166,2167],{"class":101},"build-system",[32,2169,2170],{"class":56},"]\n",[32,2172,2173,2176,2179,2181,2184],{"class":34,"line":42},[32,2174,2175],{"class":56},"requires = [",[32,2177,2178],{"class":123},"\"setuptools\"",[32,2180,198],{"class":56},[32,2182,2183],{"class":123},"\"Cython\"",[32,2185,2170],{"class":56},[32,2187,2188,2191],{"class":34,"line":49},[32,2189,2190],{"class":56},"build-backend = ",[32,2192,2193],{"class":123},"\"setuptools.build_meta\"\n",[19,2195,2197],{"language":2196},"bash",[23,2198,2201],{"className":2199,"code":2200,"language":2196,"meta":27,"style":27},"language-bash shiki shiki-themes github-light github-dark","cythonize -i fib.pyx    # compiles fib.pyx into a native .so\u002F.pyd extension module\n",[29,2202,2203],{"__ignoreMap":27},[32,2204,2205,2208,2211,2214],{"class":34,"line":35},[32,2206,2207],{"class":101},"cythonize",[32,2209,2210],{"class":108}," -i",[32,2212,2213],{"class":123}," fib.pyx",[32,2215,2216],{"class":38},"    # compiles fib.pyx into a native .so\u002F.pyd extension module\n",[1511,2218,2219,2220,2223,2224,2227,2228,2231,2232,2234,2235,2238,2239,2241,2242,2245],{},"Cython compiles Python-like syntax (with optional C-level type annotations via ",[29,2221,2222],{},"cdef",") directly to C, then to a native shared library importable exactly like a normal Python module — the ",[29,2225,2226],{},"cdef int"," declarations let the generated C skip Python's dynamic type dispatch entirely for that variable, closing most of the gap to hand-written C. ",[1699,2229,2230],{},"When to reach for it",": after profiling identifies a specific, narrow hot loop that dominates runtime and can't be vectorized with ",[29,2233,1727],{}," — Cython is a scalpel for that one function, not a wholesale rewrite strategy. Numba (",[29,2236,2237],{},"@numba.jit",") is a lighter-weight alternative for numerical code: it JIT-compiles ordinary Python\u002F",[29,2240,1727],{}," functions at first call, no separate build step or ",[29,2243,2244],{},".pyx"," file needed, at the cost of being more limited in what Python features it supports.",[14,2247,2249],{"id":2248},"memory-views-avoiding-copies","Memory Views: Avoiding Copies",[19,2251,2252],{"language":21},[23,2253,2255],{"className":25,"code":2254,"language":21,"meta":27,"style":27},"data = bytearray(b\"hello world\" * 1000)\n\n# WRONG for large data — slicing a bytearray\u002Fbytes COPIES the sliced portion\nchunk = data[0:100]      # allocates a brand new 100-byte object\n\n# RIGHT — memoryview exposes a window into the SAME underlying buffer, zero-copy\nview = memoryview(data)\nchunk_view = view[0:100]     # no copy — just a new view object referencing the same memory\nchunk_view[0] = ord(\"H\")     # mutates the ORIGINAL bytearray through the view\nprint(data[:5])               # bytearray(b'Hello') — the underlying buffer changed\n",[29,2256,2257,2282,2286,2291,2315,2319,2324,2337,2359,2384],{"__ignoreMap":27},[32,2258,2259,2261,2263,2266,2268,2271,2274,2277,2280],{"class":34,"line":35},[32,2260,1546],{"class":56},[32,2262,133],{"class":52},[32,2264,2265],{"class":108}," bytearray",[32,2267,192],{"class":56},[32,2269,2270],{"class":52},"b",[32,2272,2273],{"class":123},"\"hello world\"",[32,2275,2276],{"class":52}," *",[32,2278,2279],{"class":108}," 1000",[32,2281,571],{"class":56},[32,2283,2284],{"class":34,"line":42},[32,2285,46],{"emptyLinePlaceholder":45},[32,2287,2288],{"class":34,"line":49},[32,2289,2290],{"class":38},"# WRONG for large data — slicing a bytearray\u002Fbytes COPIES the sliced portion\n",[32,2292,2293,2296,2298,2301,2303,2306,2309,2312],{"class":34,"line":60},[32,2294,2295],{"class":56},"chunk ",[32,2297,133],{"class":52},[32,2299,2300],{"class":56}," data[",[32,2302,157],{"class":108},[32,2304,2305],{"class":56},":",[32,2307,2308],{"class":108},"100",[32,2310,2311],{"class":56},"]      ",[32,2313,2314],{"class":38},"# allocates a brand new 100-byte object\n",[32,2316,2317],{"class":34,"line":68},[32,2318,46],{"emptyLinePlaceholder":45},[32,2320,2321],{"class":34,"line":76},[32,2322,2323],{"class":38},"# RIGHT — memoryview exposes a window into the SAME underlying buffer, zero-copy\n",[32,2325,2326,2329,2331,2334],{"class":34,"line":90},[32,2327,2328],{"class":56},"view ",[32,2330,133],{"class":52},[32,2332,2333],{"class":108}," memoryview",[32,2335,2336],{"class":56},"(data)\n",[32,2338,2339,2342,2344,2347,2349,2351,2353,2356],{"class":34,"line":95},[32,2340,2341],{"class":56},"chunk_view ",[32,2343,133],{"class":52},[32,2345,2346],{"class":56}," view[",[32,2348,157],{"class":108},[32,2350,2305],{"class":56},[32,2352,2308],{"class":108},[32,2354,2355],{"class":56},"]     ",[32,2357,2358],{"class":38},"# no copy — just a new view object referencing the same memory\n",[32,2360,2361,2364,2366,2368,2370,2373,2375,2378,2381],{"class":34,"line":120},[32,2362,2363],{"class":56},"chunk_view[",[32,2365,157],{"class":108},[32,2367,142],{"class":56},[32,2369,133],{"class":52},[32,2371,2372],{"class":108}," ord",[32,2374,192],{"class":56},[32,2376,2377],{"class":123},"\"H\"",[32,2379,2380],{"class":56},")     ",[32,2382,2383],{"class":38},"# mutates the ORIGINAL bytearray through the view\n",[32,2385,2386,2388,2391,2393,2396],{"class":34,"line":127},[32,2387,1245],{"class":108},[32,2389,2390],{"class":56},"(data[:",[32,2392,965],{"class":108},[32,2394,2395],{"class":56},"])               ",[32,2397,2398],{"class":38},"# bytearray(b'Hello') — the underlying buffer changed\n",[1511,2400,2401,2402,2405,2406,2409,2410,2412,2413,2416,2417,2420,2421,2423,2424,2426,2427,1416,2430,2432],{},"A ",[29,2403,2404],{},"memoryview"," implements Python's buffer protocol, letting code operate on a slice, reshape, or cast a large binary buffer (a ",[29,2407,2408],{},"bytearray",", a ",[29,2411,1727],{}," array, an ",[29,2414,2415],{},"array.array",") without copying the underlying bytes — critical when processing large files or network buffers where naive slicing (",[29,2418,2419],{},"data[a:b]",") would otherwise allocate a new copy for every slice operation. ",[1699,2422,1701],{},": reach for ",[29,2425,2404],{}," when repeatedly slicing large ",[29,2428,2429],{},"bytes",[29,2431,2408],{}," objects in a hot path (binary protocol parsers, file-chunk processing) — the memory and time savings scale directly with how large the buffer and how frequent the slicing is.",[19,2434,2435],{"language":21},[23,2436,2438],{"className":25,"code":2437,"language":21,"meta":27,"style":27},"def parse_header_wrong(packet: bytes):\n    magic = packet[0:4]        # copies 4 bytes into a new bytes object\n    length = packet[4:8]       # copies another 4 bytes\n    payload = packet[8:]       # copies the ENTIRE remaining payload — the expensive one\n    return magic, length, payload\n\ndef parse_header_fast(packet: bytes):\n    view = memoryview(packet)\n    magic = view[0:4]          # zero-copy view\n    length = view[4:8]         # zero-copy view\n    payload = view[8:]          # zero-copy view, regardless of payload size\n    return magic, length, payload\n",[29,2439,2440,2454,2477,2499,2516,2523,2527,2540,2552,2572,2591,2607],{"__ignoreMap":27},[32,2441,2442,2444,2447,2450,2452],{"class":34,"line":35},[32,2443,98],{"class":52},[32,2445,2446],{"class":101}," parse_header_wrong",[32,2448,2449],{"class":56},"(packet: ",[32,2451,2429],{"class":108},[32,2453,221],{"class":56},[32,2455,2456,2459,2461,2464,2466,2468,2471,2474],{"class":34,"line":42},[32,2457,2458],{"class":56},"    magic ",[32,2460,133],{"class":52},[32,2462,2463],{"class":56}," packet[",[32,2465,157],{"class":108},[32,2467,2305],{"class":56},[32,2469,2470],{"class":108},"4",[32,2472,2473],{"class":56},"]        ",[32,2475,2476],{"class":38},"# copies 4 bytes into a new bytes object\n",[32,2478,2479,2482,2484,2486,2488,2490,2493,2496],{"class":34,"line":49},[32,2480,2481],{"class":56},"    length ",[32,2483,133],{"class":52},[32,2485,2463],{"class":56},[32,2487,2470],{"class":108},[32,2489,2305],{"class":56},[32,2491,2492],{"class":108},"8",[32,2494,2495],{"class":56},"]       ",[32,2497,2498],{"class":38},"# copies another 4 bytes\n",[32,2500,2501,2504,2506,2508,2510,2513],{"class":34,"line":60},[32,2502,2503],{"class":56},"    payload ",[32,2505,133],{"class":52},[32,2507,2463],{"class":56},[32,2509,2492],{"class":108},[32,2511,2512],{"class":56},":]       ",[32,2514,2515],{"class":38},"# copies the ENTIRE remaining payload — the expensive one\n",[32,2517,2518,2520],{"class":34,"line":68},[32,2519,267],{"class":52},[32,2521,2522],{"class":56}," magic, length, payload\n",[32,2524,2525],{"class":34,"line":76},[32,2526,46],{"emptyLinePlaceholder":45},[32,2528,2529,2531,2534,2536,2538],{"class":34,"line":90},[32,2530,98],{"class":52},[32,2532,2533],{"class":101}," parse_header_fast",[32,2535,2449],{"class":56},[32,2537,2429],{"class":108},[32,2539,221],{"class":56},[32,2541,2542,2545,2547,2549],{"class":34,"line":95},[32,2543,2544],{"class":56},"    view ",[32,2546,133],{"class":52},[32,2548,2333],{"class":108},[32,2550,2551],{"class":56},"(packet)\n",[32,2553,2554,2556,2558,2560,2562,2564,2566,2569],{"class":34,"line":120},[32,2555,2458],{"class":56},[32,2557,133],{"class":52},[32,2559,2346],{"class":56},[32,2561,157],{"class":108},[32,2563,2305],{"class":56},[32,2565,2470],{"class":108},[32,2567,2568],{"class":56},"]          ",[32,2570,2571],{"class":38},"# zero-copy view\n",[32,2573,2574,2576,2578,2580,2582,2584,2586,2589],{"class":34,"line":127},[32,2575,2481],{"class":56},[32,2577,133],{"class":52},[32,2579,2346],{"class":56},[32,2581,2470],{"class":108},[32,2583,2305],{"class":56},[32,2585,2492],{"class":108},[32,2587,2588],{"class":56},"]         ",[32,2590,2571],{"class":38},[32,2592,2593,2595,2597,2599,2601,2604],{"class":34,"line":151},[32,2594,2503],{"class":56},[32,2596,133],{"class":52},[32,2598,2346],{"class":56},[32,2600,2492],{"class":108},[32,2602,2603],{"class":56},":]          ",[32,2605,2606],{"class":38},"# zero-copy view, regardless of payload size\n",[32,2608,2609,2611],{"class":34,"line":177},[32,2610,267],{"class":52},[32,2612,2522],{"class":56},[14,2614,2616,2619],{"id":2615},"__slots__-trading-flexibility-for-memory",[29,2617,2618],{},"__slots__",": Trading Flexibility for Memory",[19,2621,2622],{"language":21},[23,2623,2625],{"className":25,"code":2624,"language":21,"meta":27,"style":27},"import sys\n\nclass PointDict:\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\nclass PointSlots:\n    __slots__ = (\"x\", \"y\")\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\np1 = PointDict(1, 2)\np2 = PointSlots(1, 2)\nprint(sys.getsizeof(p1.__dict__))   # 296 bytes or more — every instance carries a full dict\nprint(sys.getsizeof(p2))              # dramatically smaller — no __dict__ at all\n",[29,2626,2627,2634,2638,2649,2660,2672,2684,2688,2697,2718,2726,2736,2746,2750,2768,2786,2802],{"__ignoreMap":27},[32,2628,2629,2631],{"class":34,"line":35},[32,2630,53],{"class":52},[32,2632,2633],{"class":56}," sys\n",[32,2635,2636],{"class":34,"line":42},[32,2637,46],{"emptyLinePlaceholder":45},[32,2639,2640,2643,2646],{"class":34,"line":49},[32,2641,2642],{"class":52},"class",[32,2644,2645],{"class":101}," PointDict",[32,2647,2648],{"class":56},":\n",[32,2650,2651,2654,2657],{"class":34,"line":60},[32,2652,2653],{"class":52},"    def",[32,2655,2656],{"class":108}," __init__",[32,2658,2659],{"class":56},"(self, x, y):\n",[32,2661,2662,2665,2668,2670],{"class":34,"line":68},[32,2663,2664],{"class":108},"        self",[32,2666,2667],{"class":56},".x ",[32,2669,133],{"class":52},[32,2671,1609],{"class":56},[32,2673,2674,2676,2679,2681],{"class":34,"line":76},[32,2675,2664],{"class":108},[32,2677,2678],{"class":56},".y ",[32,2680,133],{"class":52},[32,2682,2683],{"class":56}," y\n",[32,2685,2686],{"class":34,"line":90},[32,2687,46],{"emptyLinePlaceholder":45},[32,2689,2690,2692,2695],{"class":34,"line":95},[32,2691,2642],{"class":52},[32,2693,2694],{"class":101}," PointSlots",[32,2696,2648],{"class":56},[32,2698,2699,2702,2705,2708,2711,2713,2716],{"class":34,"line":120},[32,2700,2701],{"class":108},"    __slots__",[32,2703,2704],{"class":52}," =",[32,2706,2707],{"class":56}," (",[32,2709,2710],{"class":123},"\"x\"",[32,2712,198],{"class":56},[32,2714,2715],{"class":123},"\"y\"",[32,2717,571],{"class":56},[32,2719,2720,2722,2724],{"class":34,"line":127},[32,2721,2653],{"class":52},[32,2723,2656],{"class":108},[32,2725,2659],{"class":56},[32,2727,2728,2730,2732,2734],{"class":34,"line":151},[32,2729,2664],{"class":108},[32,2731,2667],{"class":56},[32,2733,133],{"class":52},[32,2735,1609],{"class":56},[32,2737,2738,2740,2742,2744],{"class":34,"line":177},[32,2739,2664],{"class":108},[32,2741,2678],{"class":56},[32,2743,133],{"class":52},[32,2745,2683],{"class":56},[32,2747,2748],{"class":34,"line":224},[32,2749,46],{"emptyLinePlaceholder":45},[32,2751,2752,2755,2757,2760,2762,2764,2766],{"class":34,"line":233},[32,2753,2754],{"class":56},"p1 ",[32,2756,133],{"class":52},[32,2758,2759],{"class":56}," PointDict(",[32,2761,167],{"class":108},[32,2763,198],{"class":56},[32,2765,195],{"class":108},[32,2767,571],{"class":56},[32,2769,2770,2773,2775,2778,2780,2782,2784],{"class":34,"line":254},[32,2771,2772],{"class":56},"p2 ",[32,2774,133],{"class":52},[32,2776,2777],{"class":56}," PointSlots(",[32,2779,167],{"class":108},[32,2781,198],{"class":56},[32,2783,195],{"class":108},[32,2785,571],{"class":56},[32,2787,2788,2790,2793,2796,2799],{"class":34,"line":264},[32,2789,1245],{"class":108},[32,2791,2792],{"class":56},"(sys.getsizeof(p1.",[32,2794,2795],{"class":108},"__dict__",[32,2797,2798],{"class":56},"))   ",[32,2800,2801],{"class":38},"# 296 bytes or more — every instance carries a full dict\n",[32,2803,2804,2806,2809],{"class":34,"line":293},[32,2805,1245],{"class":108},[32,2807,2808],{"class":56},"(sys.getsizeof(p2))              ",[32,2810,2811],{"class":38},"# dramatically smaller — no __dict__ at all\n",[1511,2813,2814,2815,2817,2818,2820,2821,2824],{},"Without ",[29,2816,2618],{},", every instance carries its own ",[29,2819,2795],{}," for attribute storage — flexible (attributes can be added dynamically at any time) but memory-heavy, since a dict's hash table overhead dwarfs the space needed for just two floats. ",[29,2822,2823],{},"__slots__ = (\"x\", \"y\")"," tells Python to allocate fixed, dict-free storage for exactly those named attributes, cutting per-instance memory dramatically (commonly 40-50% for simple attribute-heavy classes) — the difference compounds fast when instantiating millions of objects (parsed rows, graph nodes, simulation particles).",[19,2826,2827],{"language":21},[23,2828,2830],{"className":25,"code":2829,"language":21,"meta":27,"style":27},"class PointSlots:\n    __slots__ = (\"x\", \"y\")\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\np = PointSlots(1, 2)\n# p.z = 3   # AttributeError: 'PointSlots' object has no attribute 'z' — no __dict__ to fall back to\n",[29,2831,2832,2840,2856,2864,2874,2884,2888,2905],{"__ignoreMap":27},[32,2833,2834,2836,2838],{"class":34,"line":35},[32,2835,2642],{"class":52},[32,2837,2694],{"class":101},[32,2839,2648],{"class":56},[32,2841,2842,2844,2846,2848,2850,2852,2854],{"class":34,"line":42},[32,2843,2701],{"class":108},[32,2845,2704],{"class":52},[32,2847,2707],{"class":56},[32,2849,2710],{"class":123},[32,2851,198],{"class":56},[32,2853,2715],{"class":123},[32,2855,571],{"class":56},[32,2857,2858,2860,2862],{"class":34,"line":49},[32,2859,2653],{"class":52},[32,2861,2656],{"class":108},[32,2863,2659],{"class":56},[32,2865,2866,2868,2870,2872],{"class":34,"line":60},[32,2867,2664],{"class":108},[32,2869,2667],{"class":56},[32,2871,133],{"class":52},[32,2873,1609],{"class":56},[32,2875,2876,2878,2880,2882],{"class":34,"line":68},[32,2877,2664],{"class":108},[32,2879,2678],{"class":56},[32,2881,133],{"class":52},[32,2883,2683],{"class":56},[32,2885,2886],{"class":34,"line":76},[32,2887,46],{"emptyLinePlaceholder":45},[32,2889,2890,2893,2895,2897,2899,2901,2903],{"class":34,"line":90},[32,2891,2892],{"class":56},"p ",[32,2894,133],{"class":52},[32,2896,2777],{"class":56},[32,2898,167],{"class":108},[32,2900,198],{"class":56},[32,2902,195],{"class":108},[32,2904,571],{"class":56},[32,2906,2907],{"class":34,"line":95},[32,2908,2909],{"class":38},"# p.z = 3   # AttributeError: 'PointSlots' object has no attribute 'z' — no __dict__ to fall back to\n",[1511,2911,2912,2915,2916,2918,2919,2921,2922,2924,2925,2927,2928,2930,2931,2934,2935,2937,2938,2940],{},[1699,2913,2914],{},"The trade-off",": ",[29,2917,2618],{}," classes cannot have arbitrary attributes added after the fact (no ",[29,2920,2795],{}," exists to hold them, unless ",[29,2923,2795],{}," is explicitly included in ",[29,2926,2618],{},", which defeats much of the memory benefit), and multiple inheritance between two classes that each define non-empty, non-identical ",[29,2929,2618],{}," raises ",[29,2932,2933],{},"TypeError: multiple bases have instance lay-out conflict",". ",[1699,2936,1701],{},": use ",[29,2939,2618],{}," for classes instantiated in bulk (thousands to millions of instances) with a fixed, known attribute set — data records, tree\u002Fgraph nodes, particle-simulation entities — and skip it for classes where dynamic attribute flexibility matters more than the memory savings.",[14,2942,2944,2947],{"id":2943},"functoolslru_cache-trading-memory-for-speed",[29,2945,2946],{},"functools.lru_cache",": Trading Memory for Speed",[19,2949,2950],{"language":21},[23,2951,2953],{"className":25,"code":2952,"language":21,"meta":27,"style":27},"from functools import lru_cache\nimport time\n\n@lru_cache(maxsize=None)\ndef expensive_lookup(user_id):\n    time.sleep(0.1)               # simulates a slow database call\n    return f\"user-{user_id}\"\n\nstart = time.perf_counter()\nexpensive_lookup(42)\nprint(f\"first call: {time.perf_counter() - start:.3f}s\")   # ~0.100s\n\nstart = time.perf_counter()\nexpensive_lookup(42)                                          # cache hit — same arguments\nprint(f\"second call: {time.perf_counter() - start:.3f}s\")    # ~0.000s\n",[29,2954,2955,2967,2973,2977,2994,3004,3018,3038,3042,3050,3060,3091,3095,3103,3115],{"__ignoreMap":27},[32,2956,2957,2959,2962,2964],{"class":34,"line":35},[32,2958,79],{"class":52},[32,2960,2961],{"class":56}," functools ",[32,2963,53],{"class":52},[32,2965,2966],{"class":56}," lru_cache\n",[32,2968,2969,2971],{"class":34,"line":42},[32,2970,53],{"class":52},[32,2972,73],{"class":56},[32,2974,2975],{"class":34,"line":49},[32,2976,46],{"emptyLinePlaceholder":45},[32,2978,2979,2982,2984,2987,2989,2992],{"class":34,"line":60},[32,2980,2981],{"class":101},"@lru_cache",[32,2983,192],{"class":56},[32,2985,2986],{"class":1195},"maxsize",[32,2988,133],{"class":52},[32,2990,2991],{"class":108},"None",[32,2993,571],{"class":56},[32,2995,2996,2998,3001],{"class":34,"line":68},[32,2997,98],{"class":52},[32,2999,3000],{"class":101}," expensive_lookup",[32,3002,3003],{"class":56},"(user_id):\n",[32,3005,3006,3009,3012,3015],{"class":34,"line":76},[32,3007,3008],{"class":56},"    time.sleep(",[32,3010,3011],{"class":108},"0.1",[32,3013,3014],{"class":56},")               ",[32,3016,3017],{"class":38},"# simulates a slow database call\n",[32,3019,3020,3022,3025,3028,3030,3033,3035],{"class":34,"line":90},[32,3021,267],{"class":52},[32,3023,3024],{"class":52}," f",[32,3026,3027],{"class":123},"\"user-",[32,3029,939],{"class":108},[32,3031,3032],{"class":56},"user_id",[32,3034,991],{"class":108},[32,3036,3037],{"class":123},"\"\n",[32,3039,3040],{"class":34,"line":95},[32,3041,46],{"emptyLinePlaceholder":45},[32,3043,3044,3046,3048],{"class":34,"line":120},[32,3045,1573],{"class":56},[32,3047,133],{"class":52},[32,3049,1215],{"class":56},[32,3051,3052,3055,3058],{"class":34,"line":127},[32,3053,3054],{"class":56},"expensive_lookup(",[32,3056,3057],{"class":108},"42",[32,3059,571],{"class":56},[32,3061,3062,3064,3066,3068,3071,3073,3075,3077,3079,3082,3084,3086,3088],{"class":34,"line":151},[32,3063,1245],{"class":108},[32,3065,192],{"class":56},[32,3067,927],{"class":52},[32,3069,3070],{"class":123},"\"first call: ",[32,3072,939],{"class":108},[32,3074,1625],{"class":56},[32,3076,760],{"class":52},[32,3078,1630],{"class":56},[32,3080,3081],{"class":52},":.3f",[32,3083,991],{"class":108},[32,3085,1637],{"class":123},[32,3087,583],{"class":56},[32,3089,3090],{"class":38},"# ~0.100s\n",[32,3092,3093],{"class":34,"line":177},[32,3094,46],{"emptyLinePlaceholder":45},[32,3096,3097,3099,3101],{"class":34,"line":224},[32,3098,1573],{"class":56},[32,3100,133],{"class":52},[32,3102,1215],{"class":56},[32,3104,3105,3107,3109,3112],{"class":34,"line":233},[32,3106,3054],{"class":56},[32,3108,3057],{"class":108},[32,3110,3111],{"class":56},")                                          ",[32,3113,3114],{"class":38},"# cache hit — same arguments\n",[32,3116,3117,3119,3121,3123,3126,3128,3130,3132,3134,3136,3138,3140,3143],{"class":34,"line":254},[32,3118,1245],{"class":108},[32,3120,192],{"class":56},[32,3122,927],{"class":52},[32,3124,3125],{"class":123},"\"second call: ",[32,3127,939],{"class":108},[32,3129,1625],{"class":56},[32,3131,760],{"class":52},[32,3133,1630],{"class":56},[32,3135,3081],{"class":52},[32,3137,991],{"class":108},[32,3139,1637],{"class":123},[32,3141,3142],{"class":56},")    ",[32,3144,3145],{"class":38},"# ~0.000s\n",[1511,3147,3148,3151,3152,2915,3155,3158,3159,3161,3162,3165],{},[29,3149,3150],{},"lru_cache"," memoizes a pure function's return value keyed by its arguments — a one-line decorator that converts a repeatedly-called, deterministic, side-effect-free function into an O(1) lookup after the first call. ",[1699,3153,3154],{},"The gotcha",[29,3156,3157],{},"maxsize=None"," means the cache grows unboundedly, which is a memory leak in long-running processes (a web server handling millions of distinct user IDs will eventually cache all of them) — set an explicit ",[29,3160,2986],{}," (e.g., ",[29,3163,3164],{},"maxsize=1024",") for anything long-running and unbounded in the input space, letting Python's LRU eviction cap memory growth.",[14,3167,3169],{"id":3168},"common-performance-pitfalls","Common Performance Pitfalls",[19,3171,3172],{"language":21},[23,3173,3175],{"className":25,"code":3174,"language":21,"meta":27,"style":27},"# WRONG — string concatenation in a loop is O(n²): each += allocates\n# an entirely new string, since str is IMMUTABLE in Python\nresult = \"\"\nfor word in [\"a very\", \"long\", \"sequence\", \"of\", \"many\", \"words\"] * 10000:\n    result += word + \" \"\n\n# RIGHT — join() allocates the final string ONCE, O(n) total\nwords = [\"a very\", \"long\", \"sequence\", \"of\", \"many\", \"words\"] * 10000\nresult = \" \".join(words)\n",[29,3176,3177,3182,3187,3197,3245,3258,3262,3267,3305],{"__ignoreMap":27},[32,3178,3179],{"class":34,"line":35},[32,3180,3181],{"class":38},"# WRONG — string concatenation in a loop is O(n²): each += allocates\n",[32,3183,3184],{"class":34,"line":42},[32,3185,3186],{"class":38},"# an entirely new string, since str is IMMUTABLE in Python\n",[32,3188,3189,3192,3194],{"class":34,"line":49},[32,3190,3191],{"class":56},"result ",[32,3193,133],{"class":52},[32,3195,3196],{"class":123}," \"\"\n",[32,3198,3199,3201,3204,3206,3208,3211,3213,3216,3218,3221,3223,3226,3228,3231,3233,3236,3238,3240,3243],{"class":34,"line":60},[32,3200,273],{"class":52},[32,3202,3203],{"class":56}," word ",[32,3205,186],{"class":52},[32,3207,136],{"class":56},[32,3209,3210],{"class":123},"\"a very\"",[32,3212,198],{"class":56},[32,3214,3215],{"class":123},"\"long\"",[32,3217,198],{"class":56},[32,3219,3220],{"class":123},"\"sequence\"",[32,3222,198],{"class":56},[32,3224,3225],{"class":123},"\"of\"",[32,3227,198],{"class":56},[32,3229,3230],{"class":123},"\"many\"",[32,3232,198],{"class":56},[32,3234,3235],{"class":123},"\"words\"",[32,3237,142],{"class":56},[32,3239,145],{"class":52},[32,3241,3242],{"class":108}," 10000",[32,3244,2648],{"class":56},[32,3246,3247,3249,3251,3253,3255],{"class":34,"line":68},[32,3248,526],{"class":56},[32,3250,1606],{"class":52},[32,3252,3203],{"class":56},[32,3254,215],{"class":52},[32,3256,3257],{"class":123}," \" \"\n",[32,3259,3260],{"class":34,"line":76},[32,3261,46],{"emptyLinePlaceholder":45},[32,3263,3264],{"class":34,"line":90},[32,3265,3266],{"class":38},"# RIGHT — join() allocates the final string ONCE, O(n) total\n",[32,3268,3269,3272,3274,3276,3278,3280,3282,3284,3286,3288,3290,3292,3294,3296,3298,3300,3302],{"class":34,"line":95},[32,3270,3271],{"class":56},"words ",[32,3273,133],{"class":52},[32,3275,136],{"class":56},[32,3277,3210],{"class":123},[32,3279,198],{"class":56},[32,3281,3215],{"class":123},[32,3283,198],{"class":56},[32,3285,3220],{"class":123},[32,3287,198],{"class":56},[32,3289,3225],{"class":123},[32,3291,198],{"class":56},[32,3293,3230],{"class":123},[32,3295,198],{"class":56},[32,3297,3235],{"class":123},[32,3299,142],{"class":56},[32,3301,145],{"class":52},[32,3303,3304],{"class":108}," 10000\n",[32,3306,3307,3309,3311,3314],{"class":34,"line":120},[32,3308,3191],{"class":56},[32,3310,133],{"class":52},[32,3312,3313],{"class":123}," \" \"",[32,3315,3316],{"class":56},".join(words)\n",[1511,3318,3319,3320,3323,3324,3327,3328,3331,3332,3335],{},"Every ",[29,3321,3322],{},"result += word"," on a Python ",[29,3325,3326],{},"str"," creates a brand-new string object and copies the entire existing content into it (strings are immutable), turning what looks like a simple loop into quadratic behavior as ",[29,3329,3330],{},"result"," grows — for large inputs, this is a common, easy-to-miss source of surprisingly slow \"obviously simple\" code. ",[29,3333,3334],{},"\"\".join(list_of_strings)"," computes the total needed length once and allocates a single buffer, making it linear.",[19,3337,3338],{"language":21},[23,3339,3341],{"className":25,"code":3340,"language":21,"meta":27,"style":27},"# WRONG — repeated membership testing against a list is O(n) PER CHECK\nblocked_ids = [101, 204, 305, 512, 630]  # imagine thousands of entries\nfor user_id in incoming_requests:\n    if user_id in blocked_ids:            # linear scan every single time\n        reject(user_id)\n\n# RIGHT — a set gives O(1) average-case membership testing\nblocked_ids = {101, 204, 305, 512, 630}\nfor user_id in incoming_requests:\n    if user_id in blocked_ids:            # O(1) hash lookup\n        reject(user_id)\n",[29,3342,3343,3348,3386,3398,3413,3418,3422,3427,3457,3467,3480],{"__ignoreMap":27},[32,3344,3345],{"class":34,"line":35},[32,3346,3347],{"class":38},"# WRONG — repeated membership testing against a list is O(n) PER CHECK\n",[32,3349,3350,3353,3355,3357,3360,3362,3365,3367,3370,3372,3375,3377,3380,3383],{"class":34,"line":42},[32,3351,3352],{"class":56},"blocked_ids ",[32,3354,133],{"class":52},[32,3356,136],{"class":56},[32,3358,3359],{"class":108},"101",[32,3361,198],{"class":56},[32,3363,3364],{"class":108},"204",[32,3366,198],{"class":56},[32,3368,3369],{"class":108},"305",[32,3371,198],{"class":56},[32,3373,3374],{"class":108},"512",[32,3376,198],{"class":56},[32,3378,3379],{"class":108},"630",[32,3381,3382],{"class":56},"]  ",[32,3384,3385],{"class":38},"# imagine thousands of entries\n",[32,3387,3388,3390,3393,3395],{"class":34,"line":49},[32,3389,273],{"class":52},[32,3391,3392],{"class":56}," user_id ",[32,3394,186],{"class":52},[32,3396,3397],{"class":56}," incoming_requests:\n",[32,3399,3400,3403,3405,3407,3410],{"class":34,"line":60},[32,3401,3402],{"class":52},"    if",[32,3404,3392],{"class":56},[32,3406,186],{"class":52},[32,3408,3409],{"class":56}," blocked_ids:            ",[32,3411,3412],{"class":38},"# linear scan every single time\n",[32,3414,3415],{"class":34,"line":68},[32,3416,3417],{"class":56},"        reject(user_id)\n",[32,3419,3420],{"class":34,"line":76},[32,3421,46],{"emptyLinePlaceholder":45},[32,3423,3424],{"class":34,"line":90},[32,3425,3426],{"class":38},"# RIGHT — a set gives O(1) average-case membership testing\n",[32,3428,3429,3431,3433,3436,3438,3440,3442,3444,3446,3448,3450,3452,3454],{"class":34,"line":95},[32,3430,3352],{"class":56},[32,3432,133],{"class":52},[32,3434,3435],{"class":56}," {",[32,3437,3359],{"class":108},[32,3439,198],{"class":56},[32,3441,3364],{"class":108},[32,3443,198],{"class":56},[32,3445,3369],{"class":108},[32,3447,198],{"class":56},[32,3449,3374],{"class":108},[32,3451,198],{"class":56},[32,3453,3379],{"class":108},[32,3455,3456],{"class":56},"}\n",[32,3458,3459,3461,3463,3465],{"class":34,"line":120},[32,3460,273],{"class":52},[32,3462,3392],{"class":56},[32,3464,186],{"class":52},[32,3466,3397],{"class":56},[32,3468,3469,3471,3473,3475,3477],{"class":34,"line":127},[32,3470,3402],{"class":52},[32,3472,3392],{"class":56},[32,3474,186],{"class":52},[32,3476,3409],{"class":56},[32,3478,3479],{"class":38},"# O(1) hash lookup\n",[32,3481,3482],{"class":34,"line":151},[32,3483,3417],{"class":56},[1511,3485,2401,3486,3489,3490,3492,3493,3496,3497,3500,3501,3503,3504,3506,3507,3510,3511,3513,3514,3516],{},[29,3487,3488],{},"list","'s ",[29,3491,186],{}," operator is a linear scan; a ",[29,3494,3495],{},"set","'s (or ",[29,3498,3499],{},"dict","'s) ",[29,3502,186],{}," operator is a hash lookup, averaging O(1) regardless of collection size. ",[1699,3505,1701],{},": any collection used primarily for membership testing (",[29,3508,3509],{},"x in collection","), not order or duplicates, should be a ",[29,3512,3495],{},", not a ",[29,3515,3488],{}," — a one-line type change with no other code changes needed, and a massive asymptotic improvement as the collection grows.",[14,3518,3520],{"id":3519},"tips-tricks","💡 Tips & Tricks",[3522,3523,3524,3537,3553,3562,3585],"ul",{},[3525,3526,3527,2915,3530,3533,3534,3536],"li",{},[1699,3528,3529],{},"Performance",[29,3531,3532],{},"python -X importtime myscript.py"," reports how long each import took at startup — useful for diagnosing slow CLI tool startup caused by an unexpectedly heavy transitive import (importing all of ",[29,3535,1930],{}," just to use one small helper function, for example).",[3525,3538,3539,2915,3542,2707,3545,3548,3549,3552],{},[1699,3540,3541],{},"Debug",[29,3543,3544],{},"timeit",[29,3546,3547],{},"python -m timeit -s \"setup code\" \"statement to time\"",") runs a snippet many times and reports the best\u002Faverage time, automatically handling GC and warm-up noise far more reliably than manually wrapping ",[29,3550,3551],{},"time.perf_counter()"," around a single run.",[3525,3554,3555,2915,3558,3561],{},[1699,3556,3557],{},"Idiom",[29,3559,3560],{},"sys.intern()"," can deduplicate memory for many repeated identical strings (e.g., column names re-parsed from millions of CSV rows) — Python already auto-interns short identifier-like string literals, but explicit interning helps for strings built dynamically at runtime that happen to repeat frequently.",[3525,3563,3564,3566,3567,3570,3571,3574,3575,1416,3578,1416,3581,3584],{},[1699,3565,3529],{},": generator expressions (",[29,3568,3569],{},"sum(x*x for x in data)",") avoid materializing an intermediate list entirely, unlike the equivalent list comprehension (",[29,3572,3573],{},"sum([x*x for x in data])",") — for a one-pass consumption like ",[29,3576,3577],{},"sum()",[29,3579,3580],{},"any()",[29,3582,3583],{},"all()",", the generator form uses O(1) memory instead of O(n).",[3525,3586,3587,2915,3589,3592],{},[1699,3588,3541],{},[29,3590,3591],{},"tracemalloc"," (standard library) snapshots memory allocations and can diff two snapshots to show exactly which lines of code are responsible for growth between them — far more precise than watching overall process RSS climb and guessing at the cause.",[14,3594,3596],{"id":3595},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[3522,3598,3599,3611,3627,3651,3673],{},[3525,3600,3601,3607,3608,3610],{},[1699,3602,3603,3606],{},[29,3604,3605],{},"cProfile","'s per-call overhead can itself distort results for functions with extremely high call counts and tiny bodies"," — a function called a million times with almost no work inside it can appear disproportionately expensive purely due to profiling instrumentation overhead, not real cost; cross-check suspiciously \"hot\" tiny functions with ",[29,3609,3544],{}," on a de-instrumented run.",[3525,3612,3613,3626],{},[1699,3614,3615,3617,3618,3621,3622,3625],{},[29,3616,3150],{}," on a method (not a plain function) keeps the cache alive as long as the ",[3619,3620,2642],"em",{}," holds a reference to the cached wrapper, which can keep ",[29,3623,3624],{},"self"," (and everything it references) alive far longer than expected"," — caching instance methods can silently create memory leaks by preventing garbage collection of otherwise-dead objects; prefer caching at the module level or explicitly scoping cache lifetime to the instance.",[3525,3628,3629,3641,3642,3644,3645,3647,3648,3650],{},[1699,3630,3631,3633,3634,3636,3637,3640],{},[29,3632,2618],{}," inherited from a base class without ",[29,3635,2618],{}," on ",[29,3638,3639],{},"object"," itself provides no memory savings"," — if any class in the MRO omits ",[29,3643,2618],{}," (defaulting to a ",[29,3646,2795],{},"), instances of subclasses still get a ",[29,3649,2795],{}," alongside the slots, silently defeating the entire optimization while making the code less flexible for no benefit.",[3525,3652,3653,3662,3663,3665,3666,3669,3670,3672],{},[1699,3654,3655,3657,3658,3661],{},[29,3656,1727],{}," operations on arrays of Python objects (",[29,3659,3660],{},"dtype=object",") get none of the vectorization speedup"," — creating a ",[29,3664,1727],{}," array from mixed-type or non-numeric Python objects falls back to storing pointers to ordinary Python objects, meaning ",[29,3667,3668],{},"arr * 2"," still dispatches through slow per-element Python-level multiplication; the speedup depends entirely on a genuine, uniform numeric ",[29,3671,1196],{},".",[3525,3674,3675,2915,3681,3684,3685,3688,3689,3692,3693,3695,3696,3698],{},[1699,3676,3677,3678,3680],{},"Copying a large ",[29,3679,1727],{}," array via slicing behaves the OPPOSITE of Python lists — basic slicing returns a VIEW, not a copy",[29,3682,3683],{},"sub = arr[10:20]"," shares memory with ",[29,3686,3687],{},"arr",", so mutating ",[29,3690,3691],{},"sub"," mutates ",[29,3694,3687],{}," too — the inverse gotcha of Python's own ",[29,3697,3488],{}," slicing (which always copies), easy to get backwards when moving between the two.",[14,3700,3702],{"id":3701},"spot-the-bug","🧠 Spot the Bug",[1511,3704,3705],{},"A function processes a large log file and is unexpectedly slow and memory-hungry in production despite looking like idiomatic Python. Find the bug.",[19,3707,3708],{"language":21},[23,3709,3711],{"className":25,"code":3710,"language":21,"meta":27,"style":27},"def count_error_lines(filename):\n    with open(filename) as f:\n        lines = f.readlines()\n    error_lines = [line for line in lines if \"ERROR\" in line]\n    return len(error_lines)\n",[29,3712,3713,3723,3739,3749,3780],{"__ignoreMap":27},[32,3714,3715,3717,3720],{"class":34,"line":35},[32,3716,98],{"class":52},[32,3718,3719],{"class":101}," count_error_lines",[32,3721,3722],{"class":56},"(filename):\n",[32,3724,3725,3728,3731,3734,3736],{"class":34,"line":42},[32,3726,3727],{"class":52},"    with",[32,3729,3730],{"class":108}," open",[32,3732,3733],{"class":56},"(filename) ",[32,3735,1054],{"class":52},[32,3737,3738],{"class":56}," f:\n",[32,3740,3741,3744,3746],{"class":34,"line":49},[32,3742,3743],{"class":56},"        lines ",[32,3745,133],{"class":52},[32,3747,3748],{"class":56}," f.readlines()\n",[32,3750,3751,3754,3756,3759,3761,3764,3766,3769,3771,3774,3777],{"class":34,"line":60},[32,3752,3753],{"class":56},"    error_lines ",[32,3755,133],{"class":52},[32,3757,3758],{"class":56}," [line ",[32,3760,273],{"class":52},[32,3762,3763],{"class":56}," line ",[32,3765,186],{"class":52},[32,3767,3768],{"class":56}," lines ",[32,3770,287],{"class":52},[32,3772,3773],{"class":123}," \"ERROR\"",[32,3775,3776],{"class":52}," in",[32,3778,3779],{"class":56}," line]\n",[32,3781,3782,3784,3787],{"class":34,"line":68},[32,3783,267],{"class":52},[32,3785,3786],{"class":108}," len",[32,3788,3789],{"class":56},"(error_lines)\n",[3791,3792,3793,3797,3815,3821,3877,3884],"details",{},[3794,3795,3796],"summary",{},"Answer",[1511,3798,3799,3802,3803,3806,3807,3810,3811,3814],{},[29,3800,3801],{},"f.readlines()"," reads the ",[1699,3804,3805],{},"entire file into memory at once"," as a list of every line, before any filtering happens — for a multi-gigabyte log file, this allocates gigabytes of memory just to hold lines that will mostly be discarded a moment later, and the list comprehension then builds a ",[3619,3808,3809],{},"second"," full list (",[29,3812,3813],{},"error_lines",") alongside it, doubling peak memory further. None of this is necessary: the function only needs a count, never the actual line contents held simultaneously.",[1511,3816,3817,3818,3820],{},"The fix iterates the file lazily, one line at a time, and uses a generator expression with ",[29,3819,3577],{}," instead of materializing any intermediate list:",[19,3822,3823],{"language":21},[23,3824,3826],{"className":25,"code":3825,"language":21,"meta":27,"style":27},"def count_error_lines(filename):\n    with open(filename) as f:\n        return sum(1 for line in f if \"ERROR\" in line)\n",[29,3827,3828,3836,3848],{"__ignoreMap":27},[32,3829,3830,3832,3834],{"class":34,"line":35},[32,3831,98],{"class":52},[32,3833,3719],{"class":101},[32,3835,3722],{"class":56},[32,3837,3838,3840,3842,3844,3846],{"class":34,"line":42},[32,3839,3727],{"class":52},[32,3841,3730],{"class":108},[32,3843,3733],{"class":56},[32,3845,1054],{"class":52},[32,3847,3738],{"class":56},[32,3849,3850,3853,3855,3857,3859,3861,3863,3865,3868,3870,3872,3874],{"class":34,"line":49},[32,3851,3852],{"class":52},"        return",[32,3854,377],{"class":108},[32,3856,192],{"class":56},[32,3858,167],{"class":108},[32,3860,384],{"class":52},[32,3862,3763],{"class":56},[32,3864,186],{"class":52},[32,3866,3867],{"class":56}," f ",[32,3869,287],{"class":52},[32,3871,3773],{"class":123},[32,3873,3776],{"class":52},[32,3875,3876],{"class":56}," line)\n",[1511,3878,3879,3880,3883],{},"Iterating a file object directly (",[29,3881,3882],{},"for line in f",") reads one line at a time from disk via a small internal buffer, never holding the whole file in memory — combined with a generator expression (no intermediate list bracket), peak memory becomes O(1) relative to file size instead of O(n).",[1511,3885,3886,2915,3889,3892,3893,3896],{},[1699,3887,3888],{},"The lesson",[29,3890,3891],{},"readlines()"," (and any function that eagerly builds a full list from a large or unbounded source) trades memory for a superficially simpler-looking loop — for anything that can be processed one item at a time, iterate lazily (the file object itself, ",[29,3894,3895],{},"csv.reader",", generator expressions) rather than materializing the entire dataset in memory first.",[14,3898,3900],{"id":3899},"key-takeaways","Key Takeaways",[3522,3902,3903,3913,3925,3930,3938,3953],{},[3525,3904,3905,3906,3908,3909,3912],{},"Always profile before optimizing — ",[29,3907,3605],{}," for function-level hotspots, ",[29,3910,3911],{},"line_profiler"," for line-level detail — since intuition about bottlenecks is frequently wrong and optimizing the wrong code wastes effort.",[3525,3914,3915,3916,198,3918,3920,3921,3924],{},"Prefer built-ins (",[29,3917,1705],{},[29,3919,1714],{},", comprehensions, ",[29,3922,3923],{},"join",") over hand-written Python loops for hot paths — they execute their iteration in C, sidestepping per-iteration bytecode dispatch overhead.",[3525,3926,3927,3929],{},[29,3928,1727],{}," vectorization is the single biggest lever for numerical workloads, routinely delivering 20-50x speedups by operating on contiguous C memory instead of boxed Python objects; Cython\u002FNumba are the scalpel for narrow, non-vectorizable hot loops.",[3525,3931,3932,3934,3935,3937],{},[29,3933,2404],{}," avoids copying large binary buffers on every slice operation; ",[29,3936,2618],{}," trades dynamic-attribute flexibility for significant per-instance memory savings at scale — both matter most when multiplied across large amounts of data or many instances.",[3525,3939,3940,3941,3943,3944,3946,3947,3949,3950,3952],{},"Classic pitfalls — ",[29,3942,3326],{}," concatenation in a loop (O(n²)), ",[29,3945,3488],{}," membership testing instead of ",[29,3948,3495],{}," (O(n) vs O(1)), and eagerly materializing large collections (",[29,3951,3891],{},") instead of iterating lazily — account for a large fraction of real-world \"why is this simple code so slow\" bugs.",[3525,3954,3955,3957,3958,3960],{},[29,3956,3150],{}," trades memory for speed on pure, deterministic functions — always set a ",[29,3959,2986],{}," in long-running processes to avoid unbounded cache growth becoming a memory leak.",[3962,3963,3964],"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 .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":27,"searchDepth":42,"depth":42,"links":3966},[3967,3968,3970,3972,3973,3974,3976,3978,3979,3980,3981,3982],{"id":16,"depth":42,"text":17},{"id":1431,"depth":42,"text":3969},"The GIL, dis, and Why Python Loops Are Slow",{"id":1724,"depth":42,"text":3971},"numpy: Vectorization Over Loops",{"id":2068,"depth":42,"text":2069},{"id":2248,"depth":42,"text":2249},{"id":2615,"depth":42,"text":3975},"__slots__: Trading Flexibility for Memory",{"id":2943,"depth":42,"text":3977},"functools.lru_cache: Trading Memory for Speed",{"id":3168,"depth":42,"text":3169},{"id":3519,"depth":42,"text":3520},{"id":3595,"depth":42,"text":3596},{"id":3701,"depth":42,"text":3702},{"id":3899,"depth":42,"text":3900},"md",{},"\u002Fpython\u002F26-performance-and-optimization",{"title":5,"description":27},"python\u002F26-performance-and-optimization","vtojhaAHu3bY85xS5Jjo5f6hFGG18fDFgpTubR2qFJA",1789924651832]