[{"data":1,"prerenderedAt":2541},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode":2534},{"id":4,"title":5,"body":6,"dateModified":2501,"datePublished":2501,"description":2502,"extension":2503,"faq":2504,"meta":2517,"navigation":194,"path":2527,"seo":2528,"slug":2530,"stem":2531,"type":2532,"__hash__":2533},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode\u002Findex.md","Speed Up openpyxl with read_only Mode",{"type":7,"value":8,"toc":2485},"minimark",[9,28,132,137,162,166,404,423,440,444,447,705,719,723,726,1125,1128,1132,1195,1198,1202,1208,1370,1380,1384,1387,1599,1606,1610,1613,1881,1889,1957,1961,2090,2094,2100,2331,2340,2344,2361,2365,2379,2383,2393,2405,2418,2431,2435,2438,2447,2450,2481],[10,11,12,13,17,18,21,22,27],"p",{},"Reading a large workbook with plain ",[14,15,16],"code",{},"load_workbook"," builds every cell as a Python object before you touch a single value. ",[14,19,20],{},"read_only=True"," replaces that with a stream: rows are parsed as you ask for them and discarded afterwards. The speed-up is substantial, and so is the list of things you give up. This guide, part of ",[23,24,26],"a",{"href":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002F","Working with Large Excel Files in Python",", covers both sides.",[29,30,38,39,38,43,38,47,38,54,38,64,38,71,38,79,38,84,38,89,38,93,38,97,38,102,38,107,38,111,38,115,38,118,38,122,38,125,38,128],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"0 0 740 246","img",[34,35],"ro-mode-t","ro-mode-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:740px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[40,41,42],"title",{"id":34},"Normal load versus read-only streaming",[44,45,46],"desc",{"id":35},"A normal load parses the whole sheet into cell objects held in memory, which allows random access, editing and saving. Read-only mode parses one row at a time and discards it, which allows forward iteration only but keeps memory flat.",[48,49],"rect",{"x":50,"y":50,"width":51,"height":52,"fill":53},"0","740","246","#ffffff",[48,55],{"x":56,"y":57,"width":58,"height":59,"rx":60,"fill":61,"stroke":62,"style":63},"20","26","330","196","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[65,66,70],"text",{"x":67,"y":68,"style":69},"185","54","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","load_workbook(path)",[48,72],{"x":73,"y":74,"width":75,"height":76,"rx":77,"fill":78},"44","70","282","42","8","#ebebfd",[65,80,83],{"x":67,"y":81,"style":82},"96","font-size:11.5px;fill:var(--text,#172033);text-anchor:middle","whole sheet held as Cell objects",[65,85,88],{"x":67,"y":86,"style":87},"134","font-size:11.5px;fill:var(--teal,#0f9488);text-anchor:middle","random access: ws[\"D5000\"]",[65,90,92],{"x":67,"y":91,"style":87},"156","edit and save",[65,94,96],{"x":67,"y":95,"style":87},"178","full styling available",[65,98,101],{"x":67,"y":99,"style":100},"204","font-size:11.5px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","memory grows with the sheet",[48,103],{"x":104,"y":57,"width":58,"height":59,"rx":60,"fill":105,"stroke":106,"style":63},"390","#d9f4f1","var(--teal,#0f9488)",[65,108,20],{"x":109,"y":68,"style":110},"555","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[48,112],{"x":113,"y":74,"width":75,"height":76,"rx":77,"fill":53,"stroke":114},"414","var(--line,#cdd5e6)",[65,116,117],{"x":109,"y":81,"style":82},"one row parsed, then discarded",[65,119,121],{"x":109,"y":86,"style":120},"font-size:11.5px;fill:var(--danger,#dc2626);text-anchor:middle","forward iteration only",[65,123,124],{"x":109,"y":91,"style":120},"cannot write or save",[65,126,127],{"x":109,"y":95,"style":120},"no style information",[65,129,131],{"x":109,"y":99,"style":130},"font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","memory stays flat",[133,134,136],"h2",{"id":135},"prerequisites","Prerequisites",[138,139,144],"pre",{"className":140,"code":141,"language":142,"meta":143,"style":143},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,145,146],{"__ignoreMap":143},[147,148,151,155,159],"span",{"class":149,"line":150},"line",1,[147,152,154],{"class":153},"sMTad","pip",[147,156,158],{"class":157},"srMev"," install",[147,160,161],{"class":157}," openpyxl\n",[133,163,165],{"id":164},"the-basic-streaming-read","The basic streaming read",[138,167,171],{"className":168,"code":169,"language":170,"meta":143,"style":143},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import load_workbook\n\nwb = load_workbook(\"large.xlsx\", read_only=True, data_only=True)\ntry:\n    ws = wb[\"Orders\"]\n    rows = ws.iter_rows(values_only=True)\n    header = list(next(rows))\n    print(\"columns:\", header)\n\n    count = 0\n    for row in rows:\n        count += 1\n    print(f\"{count:,} data row(s)\")\nfinally:\n    wb.close()\n","python",[14,172,173,189,196,236,245,262,282,302,316,321,332,347,359,390,398],{"__ignoreMap":143},[147,174,175,179,183,186],{"class":149,"line":150},[147,176,178],{"class":177},"s-kum","from",[147,180,182],{"class":181},"skGVy"," openpyxl ",[147,184,185],{"class":177},"import",[147,187,188],{"class":181}," load_workbook\n",[147,190,192],{"class":149,"line":191},2,[147,193,195],{"emptyLinePlaceholder":194},true,"\n",[147,197,199,202,205,208,211,214,218,220,224,226,229,231,233],{"class":149,"line":198},3,[147,200,201],{"class":181},"wb ",[147,203,204],{"class":177},"=",[147,206,207],{"class":181}," load_workbook(",[147,209,210],{"class":157},"\"large.xlsx\"",[147,212,213],{"class":181},", ",[147,215,217],{"class":216},"sa561","read_only",[147,219,204],{"class":177},[147,221,223],{"class":222},"sP0c6","True",[147,225,213],{"class":181},[147,227,228],{"class":216},"data_only",[147,230,204],{"class":177},[147,232,223],{"class":222},[147,234,235],{"class":181},")\n",[147,237,239,242],{"class":149,"line":238},4,[147,240,241],{"class":177},"try",[147,243,244],{"class":181},":\n",[147,246,248,251,253,256,259],{"class":149,"line":247},5,[147,249,250],{"class":181},"    ws ",[147,252,204],{"class":177},[147,254,255],{"class":181}," wb[",[147,257,258],{"class":157},"\"Orders\"",[147,260,261],{"class":181},"]\n",[147,263,265,268,270,273,276,278,280],{"class":149,"line":264},6,[147,266,267],{"class":181},"    rows ",[147,269,204],{"class":177},[147,271,272],{"class":181}," ws.iter_rows(",[147,274,275],{"class":216},"values_only",[147,277,204],{"class":177},[147,279,223],{"class":222},[147,281,235],{"class":181},[147,283,285,288,290,293,296,299],{"class":149,"line":284},7,[147,286,287],{"class":181},"    header ",[147,289,204],{"class":177},[147,291,292],{"class":222}," list",[147,294,295],{"class":181},"(",[147,297,298],{"class":222},"next",[147,300,301],{"class":181},"(rows))\n",[147,303,305,308,310,313],{"class":149,"line":304},8,[147,306,307],{"class":222},"    print",[147,309,295],{"class":181},[147,311,312],{"class":157},"\"columns:\"",[147,314,315],{"class":181},", header)\n",[147,317,319],{"class":149,"line":318},9,[147,320,195],{"emptyLinePlaceholder":194},[147,322,324,327,329],{"class":149,"line":323},10,[147,325,326],{"class":181},"    count ",[147,328,204],{"class":177},[147,330,331],{"class":222}," 0\n",[147,333,335,338,341,344],{"class":149,"line":334},11,[147,336,337],{"class":177},"    for",[147,339,340],{"class":181}," row ",[147,342,343],{"class":177},"in",[147,345,346],{"class":181}," rows:\n",[147,348,350,353,356],{"class":149,"line":349},12,[147,351,352],{"class":181},"        count ",[147,354,355],{"class":177},"+=",[147,357,358],{"class":222}," 1\n",[147,360,362,364,366,369,372,376,379,382,385,388],{"class":149,"line":361},13,[147,363,307],{"class":222},[147,365,295],{"class":181},[147,367,368],{"class":177},"f",[147,370,371],{"class":157},"\"",[147,373,375],{"class":374},"sSjpA","{",[147,377,378],{"class":181},"count",[147,380,381],{"class":177},":,",[147,383,384],{"class":374},"}",[147,386,387],{"class":157}," data row(s)\"",[147,389,235],{"class":181},[147,391,393,396],{"class":149,"line":392},14,[147,394,395],{"class":177},"finally",[147,397,244],{"class":181},[147,399,401],{"class":149,"line":400},15,[147,402,403],{"class":181},"    wb.close()\n",[10,405,406,407,409,410,413,414,418,419,422],{},"Three arguments are doing the work. ",[14,408,20],{}," selects the streaming reader. ",[14,411,412],{},"data_only=True"," returns Excel's cached results rather than formula text, which is what you want when harvesting numbers — see ",[23,415,417],{"href":416},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002F","reading formula results"," for the caveats. And ",[14,420,421],{},"values_only=True"," on the iterator yields plain tuples instead of cell objects.",[10,424,425,426,428,429,431,432,435,436,439],{},"The ",[14,427,241],{},"\u002F",[14,430,395],{}," is not decoration. A read-only workbook keeps the ",[14,433,434],{},".xlsx"," archive open for as long as it might read from it, so a loop over a folder of files without ",[14,437,438],{},"close()"," exhausts the process's file handles after a few hundred iterations.",[133,441,443],{"id":442},"what-values_only-saves","What values_only saves",[10,445,446],{},"The flag looks minor and is the single biggest lever in the whole mode:",[138,448,450],{"className":168,"code":449,"language":170,"meta":143,"style":143},"import time\nfrom openpyxl import load_workbook\n\ndef timed(label, values_only):\n    wb = load_workbook(\"large.xlsx\", read_only=True, data_only=True)\n    ws = wb[\"Orders\"]\n    start = time.perf_counter()\n    total = 0\n    for row in ws.iter_rows(min_row=2, values_only=values_only):\n        cell = row[4]\n        total += (cell if values_only else cell.value) or 0\n    wb.close()\n    print(f\"{label:22} {time.perf_counter() - start:5.1f}s  total={total:,.0f}\")\n\ntimed(\"values_only=True\", True)\ntimed(\"values_only=False\", False)\n",[14,451,452,459,469,473,485,514,526,536,545,572,587,614,618,672,676,690],{"__ignoreMap":143},[147,453,454,456],{"class":149,"line":150},[147,455,185],{"class":177},[147,457,458],{"class":181}," time\n",[147,460,461,463,465,467],{"class":149,"line":191},[147,462,178],{"class":177},[147,464,182],{"class":181},[147,466,185],{"class":177},[147,468,188],{"class":181},[147,470,471],{"class":149,"line":198},[147,472,195],{"emptyLinePlaceholder":194},[147,474,475,478,482],{"class":149,"line":238},[147,476,477],{"class":177},"def",[147,479,481],{"class":480},"s_Opv"," timed",[147,483,484],{"class":181},"(label, values_only):\n",[147,486,487,490,492,494,496,498,500,502,504,506,508,510,512],{"class":149,"line":247},[147,488,489],{"class":181},"    wb ",[147,491,204],{"class":177},[147,493,207],{"class":181},[147,495,210],{"class":157},[147,497,213],{"class":181},[147,499,217],{"class":216},[147,501,204],{"class":177},[147,503,223],{"class":222},[147,505,213],{"class":181},[147,507,228],{"class":216},[147,509,204],{"class":177},[147,511,223],{"class":222},[147,513,235],{"class":181},[147,515,516,518,520,522,524],{"class":149,"line":264},[147,517,250],{"class":181},[147,519,204],{"class":177},[147,521,255],{"class":181},[147,523,258],{"class":157},[147,525,261],{"class":181},[147,527,528,531,533],{"class":149,"line":284},[147,529,530],{"class":181},"    start ",[147,532,204],{"class":177},[147,534,535],{"class":181}," time.perf_counter()\n",[147,537,538,541,543],{"class":149,"line":304},[147,539,540],{"class":181},"    total ",[147,542,204],{"class":177},[147,544,331],{"class":222},[147,546,547,549,551,553,555,558,560,563,565,567,569],{"class":149,"line":318},[147,548,337],{"class":177},[147,550,340],{"class":181},[147,552,343],{"class":177},[147,554,272],{"class":181},[147,556,557],{"class":216},"min_row",[147,559,204],{"class":177},[147,561,562],{"class":222},"2",[147,564,213],{"class":181},[147,566,275],{"class":216},[147,568,204],{"class":177},[147,570,571],{"class":181},"values_only):\n",[147,573,574,577,579,582,585],{"class":149,"line":323},[147,575,576],{"class":181},"        cell ",[147,578,204],{"class":177},[147,580,581],{"class":181}," row[",[147,583,584],{"class":222},"4",[147,586,261],{"class":181},[147,588,589,592,594,597,600,603,606,609,612],{"class":149,"line":334},[147,590,591],{"class":181},"        total ",[147,593,355],{"class":177},[147,595,596],{"class":181}," (cell ",[147,598,599],{"class":177},"if",[147,601,602],{"class":181}," values_only ",[147,604,605],{"class":177},"else",[147,607,608],{"class":181}," cell.value) ",[147,610,611],{"class":177},"or",[147,613,331],{"class":222},[147,615,616],{"class":149,"line":349},[147,617,403],{"class":181},[147,619,620,622,624,626,628,630,633,636,638,641,644,647,650,653,655,658,660,663,666,668,670],{"class":149,"line":361},[147,621,307],{"class":222},[147,623,295],{"class":181},[147,625,368],{"class":177},[147,627,371],{"class":157},[147,629,375],{"class":374},[147,631,632],{"class":181},"label",[147,634,635],{"class":177},":22",[147,637,384],{"class":374},[147,639,640],{"class":374}," {",[147,642,643],{"class":181},"time.perf_counter() ",[147,645,646],{"class":177},"-",[147,648,649],{"class":181}," start",[147,651,652],{"class":177},":5.1f",[147,654,384],{"class":374},[147,656,657],{"class":157},"s  total=",[147,659,375],{"class":374},[147,661,662],{"class":181},"total",[147,664,665],{"class":177},":,.0f",[147,667,384],{"class":374},[147,669,371],{"class":157},[147,671,235],{"class":181},[147,673,674],{"class":149,"line":392},[147,675,195],{"emptyLinePlaceholder":194},[147,677,678,681,684,686,688],{"class":149,"line":400},[147,679,680],{"class":181},"timed(",[147,682,683],{"class":157},"\"values_only=True\"",[147,685,213],{"class":181},[147,687,223],{"class":222},[147,689,235],{"class":181},[147,691,693,695,698,700,703],{"class":149,"line":692},16,[147,694,680],{"class":181},[147,696,697],{"class":157},"\"values_only=False\"",[147,699,213],{"class":181},[147,701,702],{"class":222},"False",[147,704,235],{"class":181},[10,706,707,708,711,712,715,716,718],{},"With ",[14,709,710],{},"values_only=False",", openpyxl constructs a ",[14,713,714],{},"ReadOnlyCell"," for every cell in every row — millions of short-lived objects, each triggering allocation and garbage collection. With it ",[14,717,223],{},", each row is a tuple of plain Python values. The only reason to leave it off is when you need a cell's coordinate or number format, which read-only mode exposes only partially anyway.",[133,720,722],{"id":721},"index-the-header-then-use-positions","Index the header, then use positions",[10,724,725],{},"Streaming gives you tuples, so column names have to be resolved once and used as indexes:",[138,727,729],{"className":168,"code":728,"language":170,"meta":143,"style":143},"from openpyxl import load_workbook\n\ndef stream_records(path, sheet, wanted):\n    wb = load_workbook(path, read_only=True, data_only=True)\n    try:\n        ws = wb[sheet]\n        rows = ws.iter_rows(values_only=True)\n        header = [str(h).strip() if h is not None else \"\" for h in next(rows)]\n        missing = [c for c in wanted if c not in header]\n        if missing:\n            raise ValueError(f\"{path}: missing column(s) {missing}\")\n        picks = {c: header.index(c) for c in wanted}\n\n        for row in rows:\n            yield {name: row[i] for name, i in picks.items()}\n    finally:\n        wb.close()\n\nrevenue = 0.0\nfor record in stream_records(\"large.xlsx\", \"Orders\", [\"Quantity\", \"Unit_Price\"]):\n    revenue += (record[\"Quantity\"] or 0) * (record[\"Unit_Price\"] or 0)\n\nprint(f\"revenue: {revenue:,.2f}\")\n",[14,730,731,741,745,755,780,787,797,814,863,897,905,940,959,963,974,992,999,1005,1010,1021,1054,1093,1098],{"__ignoreMap":143},[147,732,733,735,737,739],{"class":149,"line":150},[147,734,178],{"class":177},[147,736,182],{"class":181},[147,738,185],{"class":177},[147,740,188],{"class":181},[147,742,743],{"class":149,"line":191},[147,744,195],{"emptyLinePlaceholder":194},[147,746,747,749,752],{"class":149,"line":198},[147,748,477],{"class":177},[147,750,751],{"class":480}," stream_records",[147,753,754],{"class":181},"(path, sheet, wanted):\n",[147,756,757,759,761,764,766,768,770,772,774,776,778],{"class":149,"line":238},[147,758,489],{"class":181},[147,760,204],{"class":177},[147,762,763],{"class":181}," load_workbook(path, ",[147,765,217],{"class":216},[147,767,204],{"class":177},[147,769,223],{"class":222},[147,771,213],{"class":181},[147,773,228],{"class":216},[147,775,204],{"class":177},[147,777,223],{"class":222},[147,779,235],{"class":181},[147,781,782,785],{"class":149,"line":247},[147,783,784],{"class":177},"    try",[147,786,244],{"class":181},[147,788,789,792,794],{"class":149,"line":264},[147,790,791],{"class":181},"        ws ",[147,793,204],{"class":177},[147,795,796],{"class":181}," wb[sheet]\n",[147,798,799,802,804,806,808,810,812],{"class":149,"line":284},[147,800,801],{"class":181},"        rows ",[147,803,204],{"class":177},[147,805,272],{"class":181},[147,807,275],{"class":216},[147,809,204],{"class":177},[147,811,223],{"class":222},[147,813,235],{"class":181},[147,815,816,819,821,824,827,830,832,835,838,841,844,847,850,853,855,857,860],{"class":149,"line":304},[147,817,818],{"class":181},"        header ",[147,820,204],{"class":177},[147,822,823],{"class":181}," [",[147,825,826],{"class":222},"str",[147,828,829],{"class":181},"(h).strip() ",[147,831,599],{"class":177},[147,833,834],{"class":181}," h ",[147,836,837],{"class":177},"is",[147,839,840],{"class":177}," not",[147,842,843],{"class":222}," None",[147,845,846],{"class":177}," else",[147,848,849],{"class":157}," \"\"",[147,851,852],{"class":177}," for",[147,854,834],{"class":181},[147,856,343],{"class":177},[147,858,859],{"class":222}," next",[147,861,862],{"class":181},"(rows)]\n",[147,864,865,868,870,873,876,879,881,884,886,888,891,894],{"class":149,"line":318},[147,866,867],{"class":181},"        missing ",[147,869,204],{"class":177},[147,871,872],{"class":181}," [c ",[147,874,875],{"class":177},"for",[147,877,878],{"class":181}," c ",[147,880,343],{"class":177},[147,882,883],{"class":181}," wanted ",[147,885,599],{"class":177},[147,887,878],{"class":181},[147,889,890],{"class":177},"not",[147,892,893],{"class":177}," in",[147,895,896],{"class":181}," header]\n",[147,898,899,902],{"class":149,"line":323},[147,900,901],{"class":177},"        if",[147,903,904],{"class":181}," missing:\n",[147,906,907,910,913,915,917,919,921,924,926,929,931,934,936,938],{"class":149,"line":334},[147,908,909],{"class":177},"            raise",[147,911,912],{"class":222}," ValueError",[147,914,295],{"class":181},[147,916,368],{"class":177},[147,918,371],{"class":157},[147,920,375],{"class":374},[147,922,923],{"class":181},"path",[147,925,384],{"class":374},[147,927,928],{"class":157},": missing column(s) ",[147,930,375],{"class":374},[147,932,933],{"class":181},"missing",[147,935,384],{"class":374},[147,937,371],{"class":157},[147,939,235],{"class":181},[147,941,942,945,947,950,952,954,956],{"class":149,"line":349},[147,943,944],{"class":181},"        picks ",[147,946,204],{"class":177},[147,948,949],{"class":181}," {c: header.index(c) ",[147,951,875],{"class":177},[147,953,878],{"class":181},[147,955,343],{"class":177},[147,957,958],{"class":181}," wanted}\n",[147,960,961],{"class":149,"line":361},[147,962,195],{"emptyLinePlaceholder":194},[147,964,965,968,970,972],{"class":149,"line":392},[147,966,967],{"class":177},"        for",[147,969,340],{"class":181},[147,971,343],{"class":177},[147,973,346],{"class":181},[147,975,976,979,982,984,987,989],{"class":149,"line":400},[147,977,978],{"class":177},"            yield",[147,980,981],{"class":181}," {name: row[i] ",[147,983,875],{"class":177},[147,985,986],{"class":181}," name, i ",[147,988,343],{"class":177},[147,990,991],{"class":181}," picks.items()}\n",[147,993,994,997],{"class":149,"line":692},[147,995,996],{"class":177},"    finally",[147,998,244],{"class":181},[147,1000,1002],{"class":149,"line":1001},17,[147,1003,1004],{"class":181},"        wb.close()\n",[147,1006,1008],{"class":149,"line":1007},18,[147,1009,195],{"emptyLinePlaceholder":194},[147,1011,1013,1016,1018],{"class":149,"line":1012},19,[147,1014,1015],{"class":181},"revenue ",[147,1017,204],{"class":177},[147,1019,1020],{"class":222}," 0.0\n",[147,1022,1024,1026,1029,1031,1034,1036,1038,1040,1043,1046,1048,1051],{"class":149,"line":1023},20,[147,1025,875],{"class":177},[147,1027,1028],{"class":181}," record ",[147,1030,343],{"class":177},[147,1032,1033],{"class":181}," stream_records(",[147,1035,210],{"class":157},[147,1037,213],{"class":181},[147,1039,258],{"class":157},[147,1041,1042],{"class":181},", [",[147,1044,1045],{"class":157},"\"Quantity\"",[147,1047,213],{"class":181},[147,1049,1050],{"class":157},"\"Unit_Price\"",[147,1052,1053],{"class":181},"]):\n",[147,1055,1057,1060,1062,1065,1067,1070,1072,1075,1078,1081,1083,1085,1087,1089,1091],{"class":149,"line":1056},21,[147,1058,1059],{"class":181},"    revenue ",[147,1061,355],{"class":177},[147,1063,1064],{"class":181}," (record[",[147,1066,1045],{"class":157},[147,1068,1069],{"class":181},"] ",[147,1071,611],{"class":177},[147,1073,1074],{"class":222}," 0",[147,1076,1077],{"class":181},") ",[147,1079,1080],{"class":177},"*",[147,1082,1064],{"class":181},[147,1084,1050],{"class":157},[147,1086,1069],{"class":181},[147,1088,611],{"class":177},[147,1090,1074],{"class":222},[147,1092,235],{"class":181},[147,1094,1096],{"class":149,"line":1095},22,[147,1097,195],{"emptyLinePlaceholder":194},[147,1099,1101,1104,1106,1108,1111,1113,1116,1119,1121,1123],{"class":149,"line":1100},23,[147,1102,1103],{"class":222},"print",[147,1105,295],{"class":181},[147,1107,368],{"class":177},[147,1109,1110],{"class":157},"\"revenue: ",[147,1112,375],{"class":374},[147,1114,1115],{"class":181},"revenue",[147,1117,1118],{"class":177},":,.2f",[147,1120,384],{"class":374},[147,1122,371],{"class":157},[147,1124,235],{"class":181},[10,1126,1127],{},"Building a small dict per row costs something, but it keeps the calling code readable and is still far cheaper than cell objects. On the hottest loops, drop the dict and use the integer indexes directly — the difference shows up above a million rows.",[133,1129,1131],{"id":1130},"the-limits-you-are-accepting","The limits you are accepting",[29,1133,38,1138,38,1141,38,1144,38,1147,38,1150,38,1154,38,1158,38,1162,38,1166,38,1169,38,1172,38,1176,38,1180,38,1183,38,1186,38,1189,38,1192],{"viewBox":1134,"role":32,"ariaLabelledBy":1135,"xmlns":36,"style":37},"0 0 740 236",[1136,1137],"ro-limits-t","ro-limits-d",[40,1139,1140],{"id":1136},"What works and what does not in read-only mode",[44,1142,1143],{"id":1137},"Working: forward row iteration, values, cached formula results and bounded column ranges. Not working: writing or saving, random cell access after iteration, styles and fills, merged-cell information and a reliable max_row before a scan.",[48,1145],{"x":50,"y":50,"width":51,"height":1146,"fill":53},"236",[48,1148],{"x":56,"y":57,"width":58,"height":1149,"rx":60,"fill":105,"stroke":106,"style":63},"188",[65,1151,1153],{"x":67,"y":68,"style":1152},"font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","works",[65,1155,1157],{"x":67,"y":1156,"style":82},"84","forward row iteration",[65,1159,1161],{"x":67,"y":1160,"style":82},"108","values and cached results",[65,1163,1165],{"x":67,"y":1164,"style":82},"132","min_row \u002F max_col bounds",[65,1167,1168],{"x":67,"y":91,"style":82},"multiple sheets, one at a time",[65,1170,1171],{"x":67,"y":1149,"style":130},"flat memory, fast",[48,1173],{"x":104,"y":57,"width":58,"height":1149,"rx":60,"fill":1174,"stroke":1175,"style":63},"#fce9e9","var(--danger,#dc2626)",[65,1177,1179],{"x":109,"y":68,"style":1178},"font-size:13px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","does not work",[65,1181,1182],{"x":109,"y":1156,"style":82},"writing or saving",[65,1184,1185],{"x":109,"y":1160,"style":82},"re-reading a passed row",[65,1187,1188],{"x":109,"y":1164,"style":82},"fonts, fills, borders",[65,1190,1191],{"x":109,"y":91,"style":82},"merged-cell details",[65,1193,1194],{"x":109,"y":1149,"style":100},"max_row may be wrong",[10,1196,1197],{},"The right-hand column decides whether the mode fits. A job that reads values and produces a summary loses nothing. A job that reads a sheet, edits three cells and saves cannot use it at all — and the usual answer there is two handles: a read-only one for the scan and a normal one for the edit.",[133,1199,1201],{"id":1200},"dimensions-are-declared-not-measured","Dimensions are declared, not measured",[10,1203,1204,1207],{},[14,1205,1206],{},"ws.max_row"," in read-only mode comes from the sheet's declared dimension, which some writers omit and others overstate after rows have been deleted:",[138,1209,1211],{"className":168,"code":1210,"language":170,"meta":143,"style":143},"from openpyxl import load_workbook\n\nwb = load_workbook(\"large.xlsx\", read_only=True)\nws = wb[\"Orders\"]\n\nprint(\"declared:\", ws.max_row, \"x\", ws.max_column)\nprint(\"recalculated:\", ws.calculate_dimension())\n\nreal_rows = sum(1 for row in ws.iter_rows(values_only=True) if any(v is not None for v in row))\nprint(\"rows with content:\", real_rows)\nwb.close()\n",[14,1212,1213,1223,1227,1247,1260,1264,1282,1294,1298,1353,1365],{"__ignoreMap":143},[147,1214,1215,1217,1219,1221],{"class":149,"line":150},[147,1216,178],{"class":177},[147,1218,182],{"class":181},[147,1220,185],{"class":177},[147,1222,188],{"class":181},[147,1224,1225],{"class":149,"line":191},[147,1226,195],{"emptyLinePlaceholder":194},[147,1228,1229,1231,1233,1235,1237,1239,1241,1243,1245],{"class":149,"line":198},[147,1230,201],{"class":181},[147,1232,204],{"class":177},[147,1234,207],{"class":181},[147,1236,210],{"class":157},[147,1238,213],{"class":181},[147,1240,217],{"class":216},[147,1242,204],{"class":177},[147,1244,223],{"class":222},[147,1246,235],{"class":181},[147,1248,1249,1252,1254,1256,1258],{"class":149,"line":238},[147,1250,1251],{"class":181},"ws ",[147,1253,204],{"class":177},[147,1255,255],{"class":181},[147,1257,258],{"class":157},[147,1259,261],{"class":181},[147,1261,1262],{"class":149,"line":247},[147,1263,195],{"emptyLinePlaceholder":194},[147,1265,1266,1268,1270,1273,1276,1279],{"class":149,"line":264},[147,1267,1103],{"class":222},[147,1269,295],{"class":181},[147,1271,1272],{"class":157},"\"declared:\"",[147,1274,1275],{"class":181},", ws.max_row, ",[147,1277,1278],{"class":157},"\"x\"",[147,1280,1281],{"class":181},", ws.max_column)\n",[147,1283,1284,1286,1288,1291],{"class":149,"line":284},[147,1285,1103],{"class":222},[147,1287,295],{"class":181},[147,1289,1290],{"class":157},"\"recalculated:\"",[147,1292,1293],{"class":181},", ws.calculate_dimension())\n",[147,1295,1296],{"class":149,"line":304},[147,1297,195],{"emptyLinePlaceholder":194},[147,1299,1300,1303,1305,1308,1310,1313,1315,1317,1319,1321,1323,1325,1327,1329,1331,1334,1337,1339,1341,1343,1345,1348,1350],{"class":149,"line":318},[147,1301,1302],{"class":181},"real_rows ",[147,1304,204],{"class":177},[147,1306,1307],{"class":222}," sum",[147,1309,295],{"class":181},[147,1311,1312],{"class":222},"1",[147,1314,852],{"class":177},[147,1316,340],{"class":181},[147,1318,343],{"class":177},[147,1320,272],{"class":181},[147,1322,275],{"class":216},[147,1324,204],{"class":177},[147,1326,223],{"class":222},[147,1328,1077],{"class":181},[147,1330,599],{"class":177},[147,1332,1333],{"class":222}," any",[147,1335,1336],{"class":181},"(v ",[147,1338,837],{"class":177},[147,1340,840],{"class":177},[147,1342,843],{"class":222},[147,1344,852],{"class":177},[147,1346,1347],{"class":181}," v ",[147,1349,343],{"class":177},[147,1351,1352],{"class":181}," row))\n",[147,1354,1355,1357,1359,1362],{"class":149,"line":323},[147,1356,1103],{"class":222},[147,1358,295],{"class":181},[147,1360,1361],{"class":157},"\"rows with content:\"",[147,1363,1364],{"class":181},", real_rows)\n",[147,1366,1367],{"class":149,"line":334},[147,1368,1369],{"class":181},"wb.close()\n",[10,1371,1372,1375,1376,1379],{},[14,1373,1374],{},"calculate_dimension()"," forces a scan, which costs a pass over the file. If you are going to iterate anyway, count as you go instead — and never size a progress bar or an allocation on ",[14,1377,1378],{},"max_row"," in this mode without checking it first.",[133,1381,1383],{"id":1382},"reading-several-sheets-safely","Reading several sheets safely",[10,1385,1386],{},"Each sheet is a separate stream, and a read-only sheet cannot be rewound. Iterate one sheet to completion before moving on, and re-open the workbook if you need a second pass:",[138,1388,1390],{"className":168,"code":1389,"language":170,"meta":143,"style":143},"from openpyxl import load_workbook\n\ndef sheet_totals(path, column=\"Unit_Price\"):\n    totals = {}\n    wb = load_workbook(path, read_only=True, data_only=True)\n    try:\n        for ws in wb.worksheets:\n            rows = ws.iter_rows(values_only=True)\n            header = list(next(rows, ()))\n            if column not in header:\n                continue\n            idx = header.index(column)\n            totals[ws.title] = sum((row[idx] or 0) for row in rows)\n    finally:\n        wb.close()\n    return totals\n\nprint(sheet_totals(\"large.xlsx\"))\n",[14,1391,1392,1402,1406,1423,1433,1457,1463,1475,1492,1508,1523,1528,1538,1565,1571,1575,1583,1587],{"__ignoreMap":143},[147,1393,1394,1396,1398,1400],{"class":149,"line":150},[147,1395,178],{"class":177},[147,1397,182],{"class":181},[147,1399,185],{"class":177},[147,1401,188],{"class":181},[147,1403,1404],{"class":149,"line":191},[147,1405,195],{"emptyLinePlaceholder":194},[147,1407,1408,1410,1413,1416,1418,1420],{"class":149,"line":198},[147,1409,477],{"class":177},[147,1411,1412],{"class":480}," sheet_totals",[147,1414,1415],{"class":181},"(path, column",[147,1417,204],{"class":177},[147,1419,1050],{"class":157},[147,1421,1422],{"class":181},"):\n",[147,1424,1425,1428,1430],{"class":149,"line":238},[147,1426,1427],{"class":181},"    totals ",[147,1429,204],{"class":177},[147,1431,1432],{"class":181}," {}\n",[147,1434,1435,1437,1439,1441,1443,1445,1447,1449,1451,1453,1455],{"class":149,"line":247},[147,1436,489],{"class":181},[147,1438,204],{"class":177},[147,1440,763],{"class":181},[147,1442,217],{"class":216},[147,1444,204],{"class":177},[147,1446,223],{"class":222},[147,1448,213],{"class":181},[147,1450,228],{"class":216},[147,1452,204],{"class":177},[147,1454,223],{"class":222},[147,1456,235],{"class":181},[147,1458,1459,1461],{"class":149,"line":264},[147,1460,784],{"class":177},[147,1462,244],{"class":181},[147,1464,1465,1467,1470,1472],{"class":149,"line":284},[147,1466,967],{"class":177},[147,1468,1469],{"class":181}," ws ",[147,1471,343],{"class":177},[147,1473,1474],{"class":181}," wb.worksheets:\n",[147,1476,1477,1480,1482,1484,1486,1488,1490],{"class":149,"line":304},[147,1478,1479],{"class":181},"            rows ",[147,1481,204],{"class":177},[147,1483,272],{"class":181},[147,1485,275],{"class":216},[147,1487,204],{"class":177},[147,1489,223],{"class":222},[147,1491,235],{"class":181},[147,1493,1494,1497,1499,1501,1503,1505],{"class":149,"line":318},[147,1495,1496],{"class":181},"            header ",[147,1498,204],{"class":177},[147,1500,292],{"class":222},[147,1502,295],{"class":181},[147,1504,298],{"class":222},[147,1506,1507],{"class":181},"(rows, ()))\n",[147,1509,1510,1513,1516,1518,1520],{"class":149,"line":323},[147,1511,1512],{"class":177},"            if",[147,1514,1515],{"class":181}," column ",[147,1517,890],{"class":177},[147,1519,893],{"class":177},[147,1521,1522],{"class":181}," header:\n",[147,1524,1525],{"class":149,"line":334},[147,1526,1527],{"class":177},"                continue\n",[147,1529,1530,1533,1535],{"class":149,"line":349},[147,1531,1532],{"class":181},"            idx ",[147,1534,204],{"class":177},[147,1536,1537],{"class":181}," header.index(column)\n",[147,1539,1540,1543,1545,1547,1550,1552,1554,1556,1558,1560,1562],{"class":149,"line":361},[147,1541,1542],{"class":181},"            totals[ws.title] ",[147,1544,204],{"class":177},[147,1546,1307],{"class":222},[147,1548,1549],{"class":181},"((row[idx] ",[147,1551,611],{"class":177},[147,1553,1074],{"class":222},[147,1555,1077],{"class":181},[147,1557,875],{"class":177},[147,1559,340],{"class":181},[147,1561,343],{"class":177},[147,1563,1564],{"class":181}," rows)\n",[147,1566,1567,1569],{"class":149,"line":392},[147,1568,996],{"class":177},[147,1570,244],{"class":181},[147,1572,1573],{"class":149,"line":400},[147,1574,1004],{"class":181},[147,1576,1577,1580],{"class":149,"line":692},[147,1578,1579],{"class":177},"    return",[147,1581,1582],{"class":181}," totals\n",[147,1584,1585],{"class":149,"line":1001},[147,1586,195],{"emptyLinePlaceholder":194},[147,1588,1589,1591,1594,1596],{"class":149,"line":1007},[147,1590,1103],{"class":222},[147,1592,1593],{"class":181},"(sheet_totals(",[147,1595,210],{"class":157},[147,1597,1598],{"class":181},"))\n",[10,1600,1601,1602,1605],{},"Calling ",[14,1603,1604],{},"iter_rows"," twice on the same read-only sheet re-parses it from the beginning, which is correct but doubles the work. Where two passes are genuinely needed — a scan to find the header row, then a scan to read it — accumulate what you need in the first pass instead.",[133,1607,1609],{"id":1608},"two-handles-when-you-need-to-read-and-write","Two handles when you need to read and write",[10,1611,1612],{},"The common real-world job is \"scan a big sheet, then write a small summary into the same workbook\". Read-only mode cannot save, so use two handles with clearly separated roles:",[138,1614,1616],{"className":168,"code":1615,"language":170,"meta":143,"style":143},"from openpyxl import load_workbook\n\n# 1. Stream the detail sheet to compute the numbers\nreader = load_workbook(\"large.xlsx\", read_only=True, data_only=True)\ntry:\n    ws = reader[\"Orders\"]\n    rows = ws.iter_rows(values_only=True)\n    header = list(next(rows))\n    qty, price = header.index(\"Quantity\"), header.index(\"Unit_Price\")\n    revenue = sum((r[qty] or 0) * (r[price] or 0) for r in rows)\nfinally:\n    reader.close()\n\n# 2. Re-open normally, but only to write a handful of cells\nwriter = load_workbook(\"large.xlsx\")\nsummary = writer.create_sheet(\"Summary\", 0)\nsummary[\"A1\"], summary[\"B1\"] = \"Total revenue\", round(revenue, 2)\nsummary[\"B1\"].number_format = \"#,##0.00\"\nwriter.save(\"large_with_summary.xlsx\")\n",[14,1617,1618,1628,1632,1638,1667,1673,1686,1702,1716,1735,1772,1778,1783,1787,1792,1805,1824,1857,1871],{"__ignoreMap":143},[147,1619,1620,1622,1624,1626],{"class":149,"line":150},[147,1621,178],{"class":177},[147,1623,182],{"class":181},[147,1625,185],{"class":177},[147,1627,188],{"class":181},[147,1629,1630],{"class":149,"line":191},[147,1631,195],{"emptyLinePlaceholder":194},[147,1633,1634],{"class":149,"line":198},[147,1635,1637],{"class":1636},"s-wDw","# 1. Stream the detail sheet to compute the numbers\n",[147,1639,1640,1643,1645,1647,1649,1651,1653,1655,1657,1659,1661,1663,1665],{"class":149,"line":238},[147,1641,1642],{"class":181},"reader ",[147,1644,204],{"class":177},[147,1646,207],{"class":181},[147,1648,210],{"class":157},[147,1650,213],{"class":181},[147,1652,217],{"class":216},[147,1654,204],{"class":177},[147,1656,223],{"class":222},[147,1658,213],{"class":181},[147,1660,228],{"class":216},[147,1662,204],{"class":177},[147,1664,223],{"class":222},[147,1666,235],{"class":181},[147,1668,1669,1671],{"class":149,"line":247},[147,1670,241],{"class":177},[147,1672,244],{"class":181},[147,1674,1675,1677,1679,1682,1684],{"class":149,"line":264},[147,1676,250],{"class":181},[147,1678,204],{"class":177},[147,1680,1681],{"class":181}," reader[",[147,1683,258],{"class":157},[147,1685,261],{"class":181},[147,1687,1688,1690,1692,1694,1696,1698,1700],{"class":149,"line":284},[147,1689,267],{"class":181},[147,1691,204],{"class":177},[147,1693,272],{"class":181},[147,1695,275],{"class":216},[147,1697,204],{"class":177},[147,1699,223],{"class":222},[147,1701,235],{"class":181},[147,1703,1704,1706,1708,1710,1712,1714],{"class":149,"line":304},[147,1705,287],{"class":181},[147,1707,204],{"class":177},[147,1709,292],{"class":222},[147,1711,295],{"class":181},[147,1713,298],{"class":222},[147,1715,301],{"class":181},[147,1717,1718,1721,1723,1726,1728,1731,1733],{"class":149,"line":318},[147,1719,1720],{"class":181},"    qty, price ",[147,1722,204],{"class":177},[147,1724,1725],{"class":181}," header.index(",[147,1727,1045],{"class":157},[147,1729,1730],{"class":181},"), header.index(",[147,1732,1050],{"class":157},[147,1734,235],{"class":181},[147,1736,1737,1739,1741,1743,1746,1748,1750,1752,1754,1757,1759,1761,1763,1765,1768,1770],{"class":149,"line":323},[147,1738,1059],{"class":181},[147,1740,204],{"class":177},[147,1742,1307],{"class":222},[147,1744,1745],{"class":181},"((r[qty] ",[147,1747,611],{"class":177},[147,1749,1074],{"class":222},[147,1751,1077],{"class":181},[147,1753,1080],{"class":177},[147,1755,1756],{"class":181}," (r[price] ",[147,1758,611],{"class":177},[147,1760,1074],{"class":222},[147,1762,1077],{"class":181},[147,1764,875],{"class":177},[147,1766,1767],{"class":181}," r ",[147,1769,343],{"class":177},[147,1771,1564],{"class":181},[147,1773,1774,1776],{"class":149,"line":334},[147,1775,395],{"class":177},[147,1777,244],{"class":181},[147,1779,1780],{"class":149,"line":349},[147,1781,1782],{"class":181},"    reader.close()\n",[147,1784,1785],{"class":149,"line":361},[147,1786,195],{"emptyLinePlaceholder":194},[147,1788,1789],{"class":149,"line":392},[147,1790,1791],{"class":1636},"# 2. Re-open normally, but only to write a handful of cells\n",[147,1793,1794,1797,1799,1801,1803],{"class":149,"line":400},[147,1795,1796],{"class":181},"writer ",[147,1798,204],{"class":177},[147,1800,207],{"class":181},[147,1802,210],{"class":157},[147,1804,235],{"class":181},[147,1806,1807,1810,1812,1815,1818,1820,1822],{"class":149,"line":692},[147,1808,1809],{"class":181},"summary ",[147,1811,204],{"class":177},[147,1813,1814],{"class":181}," writer.create_sheet(",[147,1816,1817],{"class":157},"\"Summary\"",[147,1819,213],{"class":181},[147,1821,50],{"class":222},[147,1823,235],{"class":181},[147,1825,1826,1829,1832,1835,1838,1840,1842,1845,1847,1850,1853,1855],{"class":149,"line":1001},[147,1827,1828],{"class":181},"summary[",[147,1830,1831],{"class":157},"\"A1\"",[147,1833,1834],{"class":181},"], summary[",[147,1836,1837],{"class":157},"\"B1\"",[147,1839,1069],{"class":181},[147,1841,204],{"class":177},[147,1843,1844],{"class":157}," \"Total revenue\"",[147,1846,213],{"class":181},[147,1848,1849],{"class":222},"round",[147,1851,1852],{"class":181},"(revenue, ",[147,1854,562],{"class":222},[147,1856,235],{"class":181},[147,1858,1859,1861,1863,1866,1868],{"class":149,"line":1007},[147,1860,1828],{"class":181},[147,1862,1837],{"class":157},[147,1864,1865],{"class":181},"].number_format ",[147,1867,204],{"class":177},[147,1869,1870],{"class":157}," \"#,##0.00\"\n",[147,1872,1873,1876,1879],{"class":149,"line":1012},[147,1874,1875],{"class":181},"writer.save(",[147,1877,1878],{"class":157},"\"large_with_summary.xlsx\"",[147,1880,235],{"class":181},[10,1882,1883,1884,1888],{},"The second load is not free — it materialises the whole workbook — so this pattern helps most when the writing step can target a ",[1885,1886,1887],"em",{},"different",", smaller file. Where the output must live beside 400,000 rows of detail, consider generating the summary workbook separately and leaving the detail file untouched, which is faster and keeps the source unmodified.",[29,1890,38,1895,38,1898,38,1901,38,1904,38,1909,38,1914,38,1918,38,1923,38,1927,38,1933,38,1938,38,1942,38,1947,38,1950,38,1953],{"viewBox":1891,"role":32,"ariaLabelledBy":1892,"xmlns":36,"style":37},"0 0 740 206",[1893,1894],"ro-two-t","ro-two-d",[40,1896,1897],{"id":1893},"A streaming reader and a small writer, side by side",[44,1899,1900],{"id":1894},"The read-only handle streams four hundred thousand rows and produces one number. The writable handle only touches a two-cell summary, ideally in a separate small workbook, so the large file never has to be held in memory for writing.",[48,1902],{"x":50,"y":50,"width":51,"height":1903,"fill":53},"206",[48,1905],{"x":1906,"y":1907,"width":1908,"height":1164,"rx":60,"fill":105,"stroke":106,"style":63},"24","36","320",[65,1910,1913],{"x":1911,"y":1912,"style":1152},"184","64","read_only handle",[65,1915,1917],{"x":1911,"y":1916,"style":82},"92","400,000 rows streamed",[65,1919,1922],{"x":1911,"y":1920,"style":1921},"114","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","closed as soon as it is done",[65,1924,1926],{"x":1911,"y":1925,"style":130},"144","output: one number",[149,1928],{"x1":1929,"y1":1930,"x2":1931,"y2":1930,"stroke":1932,"style":63},"348","102","386","var(--muted,#5b6780)",[1934,1935],"polygon",{"points":1936,"fill":1937},"390,102 378,96 378,108","#5b6780",[48,1939],{"x":1940,"y":1907,"width":1941,"height":1164,"rx":60,"fill":61,"stroke":62,"style":63},"392","324",[65,1943,1946],{"x":1944,"y":1912,"style":1945},"554","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","writable handle",[65,1948,1949],{"x":1944,"y":1916,"style":82},"two cells written",[65,1951,1952],{"x":1944,"y":1920,"style":1921},"smaller file where possible",[65,1954,1956],{"x":1944,"y":1925,"style":1955},"font-size:11.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","output: summary.xlsx",[133,1958,1960],{"id":1959},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[1962,1963,1964,1980],"table",{},[1965,1966,1967],"thead",{},[1968,1969,1970,1974,1977],"tr",{},[1971,1972,1973],"th",{},"Symptom",[1971,1975,1976],{},"Cause",[1971,1978,1979],{},"Fix",[1981,1982,1983,2003,2017,2028,2043,2057,2072],"tbody",{},[1968,1984,1985,1991,1994],{},[1986,1987,1988],"td",{},[14,1989,1990],{},"Too many open files",[1986,1992,1993],{},"Read-only workbooks not closed",[1986,1995,1996,1999,2000,2002],{},[14,1997,1998],{},"wb.close()"," in a ",[14,2001,395],{}," block",[1968,2004,2005,2011,2014],{},[1986,2006,2007,2010],{},[14,2008,2009],{},"ReadOnlyWorksheet has no attribute"," for a style",[1986,2012,2013],{},"Styles are unavailable in this mode",[1986,2015,2016],{},"Use a normal load for styling work",[1968,2018,2019,2022,2025],{},[1986,2020,2021],{},"Rows already consumed \u002F empty second loop",[1986,2023,2024],{},"The stream cannot be rewound",[1986,2026,2027],{},"Re-open the workbook, or gather in one pass",[1968,2029,2030,2035,2038],{},[1986,2031,2032,2034],{},[14,2033,1378],{}," far larger than the data",[1986,2036,2037],{},"Declared dimension is stale",[1986,2039,2040,2042],{},[14,2041,1374],{}," or count while iterating",[1968,2044,2045,2051,2054],{},[1986,2046,2047,2050],{},[14,2048,2049],{},"AttributeError"," on assignment",[1986,2052,2053],{},"Read-only cells cannot be written",[1986,2055,2056],{},"Open a second, writable handle",[1968,2058,2059,2062,2067],{},[1986,2060,2061],{},"Formula text where numbers were expected",[1986,2063,2064,2066],{},[14,2065,228],{}," not set",[1986,2068,2069,2070],{},"Add ",[14,2071,412],{},[1968,2073,2074,2077,2082],{},[1986,2075,2076],{},"Slower than expected",[1986,2078,2079,2081],{},[14,2080,275],{}," left off",[1986,2083,2084,2085,2087,2088],{},"Pass ",[14,2086,421],{}," to ",[14,2089,1604],{},[133,2091,2093],{"id":2092},"a-reusable-streaming-helper","A reusable streaming helper",[10,2095,2096,2097,2099],{},"Wrapping the mode in a context manager removes every chance of forgetting ",[14,2098,438],{},", and gives the rest of your code one obvious way to read a big sheet:",[138,2101,2103],{"className":168,"code":2102,"language":170,"meta":143,"style":143},"from contextlib import contextmanager\nfrom openpyxl import load_workbook\n\n@contextmanager\ndef streamed_rows(path, sheet, first_row=2, min_col=None, max_col=None):\n    wb = load_workbook(path, read_only=True, data_only=True)\n    try:\n        ws = wb[sheet]\n        yield ws.iter_rows(min_row=first_row, min_col=min_col, max_col=max_col, values_only=True)\n    finally:\n        wb.close()\n\nwith streamed_rows(\"large.xlsx\", \"Orders\", max_col=6) as rows:\n    highest = max((r[5] or 0) for r in rows)\nprint(\"largest unit price:\", highest)\n",[14,2104,2105,2117,2127,2131,2136,2167,2191,2197,2205,2243,2249,2253,2257,2287,2319],{"__ignoreMap":143},[147,2106,2107,2109,2112,2114],{"class":149,"line":150},[147,2108,178],{"class":177},[147,2110,2111],{"class":181}," contextlib ",[147,2113,185],{"class":177},[147,2115,2116],{"class":181}," contextmanager\n",[147,2118,2119,2121,2123,2125],{"class":149,"line":191},[147,2120,178],{"class":177},[147,2122,182],{"class":181},[147,2124,185],{"class":177},[147,2126,188],{"class":181},[147,2128,2129],{"class":149,"line":198},[147,2130,195],{"emptyLinePlaceholder":194},[147,2132,2133],{"class":149,"line":238},[147,2134,2135],{"class":480},"@contextmanager\n",[147,2137,2138,2140,2143,2146,2148,2150,2153,2155,2158,2161,2163,2165],{"class":149,"line":247},[147,2139,477],{"class":177},[147,2141,2142],{"class":480}," streamed_rows",[147,2144,2145],{"class":181},"(path, sheet, first_row",[147,2147,204],{"class":177},[147,2149,562],{"class":222},[147,2151,2152],{"class":181},", min_col",[147,2154,204],{"class":177},[147,2156,2157],{"class":222},"None",[147,2159,2160],{"class":181},", max_col",[147,2162,204],{"class":177},[147,2164,2157],{"class":222},[147,2166,1422],{"class":181},[147,2168,2169,2171,2173,2175,2177,2179,2181,2183,2185,2187,2189],{"class":149,"line":264},[147,2170,489],{"class":181},[147,2172,204],{"class":177},[147,2174,763],{"class":181},[147,2176,217],{"class":216},[147,2178,204],{"class":177},[147,2180,223],{"class":222},[147,2182,213],{"class":181},[147,2184,228],{"class":216},[147,2186,204],{"class":177},[147,2188,223],{"class":222},[147,2190,235],{"class":181},[147,2192,2193,2195],{"class":149,"line":284},[147,2194,784],{"class":177},[147,2196,244],{"class":181},[147,2198,2199,2201,2203],{"class":149,"line":304},[147,2200,791],{"class":181},[147,2202,204],{"class":177},[147,2204,796],{"class":181},[147,2206,2207,2210,2212,2214,2216,2219,2222,2224,2227,2230,2232,2235,2237,2239,2241],{"class":149,"line":318},[147,2208,2209],{"class":177},"        yield",[147,2211,272],{"class":181},[147,2213,557],{"class":216},[147,2215,204],{"class":177},[147,2217,2218],{"class":181},"first_row, ",[147,2220,2221],{"class":216},"min_col",[147,2223,204],{"class":177},[147,2225,2226],{"class":181},"min_col, ",[147,2228,2229],{"class":216},"max_col",[147,2231,204],{"class":177},[147,2233,2234],{"class":181},"max_col, ",[147,2236,275],{"class":216},[147,2238,204],{"class":177},[147,2240,223],{"class":222},[147,2242,235],{"class":181},[147,2244,2245,2247],{"class":149,"line":323},[147,2246,996],{"class":177},[147,2248,244],{"class":181},[147,2250,2251],{"class":149,"line":334},[147,2252,1004],{"class":181},[147,2254,2255],{"class":149,"line":349},[147,2256,195],{"emptyLinePlaceholder":194},[147,2258,2259,2262,2265,2267,2269,2271,2273,2275,2277,2280,2282,2285],{"class":149,"line":361},[147,2260,2261],{"class":177},"with",[147,2263,2264],{"class":181}," streamed_rows(",[147,2266,210],{"class":157},[147,2268,213],{"class":181},[147,2270,258],{"class":157},[147,2272,213],{"class":181},[147,2274,2229],{"class":216},[147,2276,204],{"class":177},[147,2278,2279],{"class":222},"6",[147,2281,1077],{"class":181},[147,2283,2284],{"class":177},"as",[147,2286,346],{"class":181},[147,2288,2289,2292,2294,2297,2300,2303,2305,2307,2309,2311,2313,2315,2317],{"class":149,"line":392},[147,2290,2291],{"class":181},"    highest ",[147,2293,204],{"class":177},[147,2295,2296],{"class":222}," max",[147,2298,2299],{"class":181},"((r[",[147,2301,2302],{"class":222},"5",[147,2304,1069],{"class":181},[147,2306,611],{"class":177},[147,2308,1074],{"class":222},[147,2310,1077],{"class":181},[147,2312,875],{"class":177},[147,2314,1767],{"class":181},[147,2316,343],{"class":177},[147,2318,1564],{"class":181},[147,2320,2321,2323,2325,2328],{"class":149,"line":400},[147,2322,1103],{"class":222},[147,2324,295],{"class":181},[147,2326,2327],{"class":157},"\"largest unit price:\"",[147,2329,2330],{"class":181},", highest)\n",[10,2332,2333,2334,2336,2337,2339],{},"Because the generator is yielded inside the ",[14,2335,2261],{},", the workbook stays open exactly as long as the caller is iterating and is closed the moment the block ends — including when the body raises. Bounding ",[14,2338,2229],{}," in the helper's signature also nudges every caller towards reading only the columns it needs.",[133,2341,2343],{"id":2342},"performance-and-scale-notes","Performance and scale notes",[10,2345,2346,2347,2349,2350,2352,2353,2355,2356,2360],{},"Streaming turns memory use from \"proportional to the sheet\" into \"proportional to one row\", which is what lets a 300 MB workbook run inside a small container. Time still scales with the number of cells parsed, so bound the columns with ",[14,2348,2221],{}," and ",[14,2351,2229],{}," and skip rows you do not need with ",[14,2354,557],{},". The remaining cost is per-value Python work: keep the loop body vectorised where you can by batching rows into DataFrames, as the ",[23,2357,2359],{"href":2358},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fread-large-excel-file-in-chunks-with-pandas\u002F","chunked reading"," pattern does.",[133,2362,2364],{"id":2363},"conclusion","Conclusion",[10,2366,2367,2369,2370,2372,2373,2375,2376,2378],{},[14,2368,20],{}," with ",[14,2371,421],{}," is the fastest way openpyxl can hand you the contents of a large sheet, and it costs you writing, random access, styles and a trustworthy ",[14,2374,1378],{},". For extraction and aggregation that trade is almost always worth making. Resolve column positions from the header once, bound the range you iterate, count rows as you go, and close the workbook in a ",[14,2377,395],{}," so a folder-sized job does not run out of file handles.",[133,2380,2382],{"id":2381},"frequently-asked-questions","Frequently asked questions",[10,2384,2385,2392],{},[2386,2387,2388,2389,2391],"strong",{},"What exactly does ",[14,2390,217],{}," mode change?","\nThe worksheet becomes a lazy iterator over the sheet XML. Cells are lightweight read-only objects created as you pass them, nothing is cached, and the sheet cannot be modified or saved.",[10,2394,2395,2401,2402,2404],{},[2386,2396,2397,2398,2400],{},"Why must I call ",[14,2399,1998],{},"?","\nA read-only workbook keeps the underlying zip archive open so it can keep reading. Without ",[14,2403,438],{},", file handles accumulate until the process runs out of them.",[10,2406,2407,2413,2414,2417],{},[2386,2408,2409,2410,2412],{},"Why is ",[14,2411,1206],{}," sometimes wrong?","\nThe dimension comes from the sheet's declared range, which some writers omit or overstate. Count rows as you iterate, or call ",[14,2415,2416],{},"ws.calculate_dimension()"," to force a scan.",[10,2419,2420,2427,2428,2430],{},[2386,2421,2422,2423,2369,2425,2400],{},"Can I combine ",[14,2424,217],{},[14,2426,228],{},"\nYes, and for harvesting numbers you usually should — ",[14,2429,412],{}," returns Excel's cached results instead of formula text.",[133,2432,2434],{"id":2433},"related","Related",[10,2436,2437],{},"Up to the parent guide:",[2439,2440,2441],"ul",{},[2442,2443,2444,2446],"li",{},[23,2445,26],{"href":25}," — where streaming sits among the other levers.",[10,2448,2449],{},"Related guides:",[2439,2451,2452,2458,2465,2472],{},[2442,2453,2454,2457],{},[23,2455,2456],{"href":2358},"Read a Large Excel File in Chunks with pandas"," — batching these streamed rows into DataFrames.",[2442,2459,2460,2464],{},[23,2461,2463],{"href":2462},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002F","Write Large DataFrames to Excel with write_only Mode"," — the same idea on the way out.",[2442,2466,2467,2471],{},[23,2468,2470],{"href":2469},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-cell-value-from-excel-with-openpyxl\u002F","Read a Cell Value from Excel with openpyxl"," — normal-mode reading, for comparison.",[2442,2473,2474,2477,2478,2480],{},[23,2475,2476],{"href":416},"Read Formula Results with openpyxl data_only"," — what ",[14,2479,228],{}," actually returns.",[2482,2483,2484],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":143,"searchDepth":191,"depth":191,"links":2486},[2487,2488,2489,2490,2491,2492,2493,2494,2495,2496,2497,2498,2499,2500],{"id":135,"depth":191,"text":136},{"id":164,"depth":191,"text":165},{"id":442,"depth":191,"text":443},{"id":721,"depth":191,"text":722},{"id":1130,"depth":191,"text":1131},{"id":1200,"depth":191,"text":1201},{"id":1382,"depth":191,"text":1383},{"id":1608,"depth":191,"text":1609},{"id":1959,"depth":191,"text":1960},{"id":2092,"depth":191,"text":2093},{"id":2342,"depth":191,"text":2343},{"id":2363,"depth":191,"text":2364},{"id":2381,"depth":191,"text":2382},{"id":2433,"depth":191,"text":2434},"2026-08-01","How openpyxl's read_only mode streams a worksheet instead of building it, what values_only=True saves, the limits you accept in exchange, and the file handle you must close.","md",[2505,2508,2511,2514],{"q":2506,"a":2507},"What exactly does read_only mode change?","The worksheet becomes a lazy iterator over the sheet XML. Cells are lightweight read-only objects created as you pass them, nothing is cached, and the sheet cannot be modified or saved.",{"q":2509,"a":2510},"Why must I call wb.close()?","A read-only workbook keeps the underlying zip archive open so it can keep reading. Without close(), file handles accumulate until the process runs out of them.",{"q":2512,"a":2513},"Why is ws.max_row sometimes wrong?","The dimension comes from the sheet's declared range, which some writers omit or overstate. Count rows as you iterate, or call ws.calculate_dimension() to force a scan.",{"q":2515,"a":2516},"Can I combine read_only with data_only?","Yes, and for harvesting numbers you usually should — data_only=True returns Excel's cached results instead of formula text.",{"breadcrumb":2518},[2519,2521,2524,2525],{"name":2520,"item":428},"Home",{"name":2522,"item":2523},"Advanced Data Transformation and Cleaning","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":26,"item":25},{"name":5,"item":2526},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode\u002F","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode",{"title":5,"description":2529},"Use load_workbook(read_only=True) to stream large sheets: values_only tuples, unreliable max_row, no styles, no writing, and why wb.close() is not optional.","speed-up-openpyxl-with-read-only-mode","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode\u002Findex","how-to","ZJF0D6XjnWD-iLOICaJHf9UWL7_E0_L1MfoUbRrJ3nA",[2535,2538],{"title":2456,"path":2536,"stem":2537,"children":-1},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fread-large-excel-file-in-chunks-with-pandas","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fread-large-excel-file-in-chunks-with-pandas\u002Findex",{"title":2463,"path":2539,"stem":2540,"children":-1},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002Findex",1785584462728]