[{"data":1,"prerenderedAt":2006},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fread-and-write-cell-ranges-with-win32com":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fread-and-write-cell-ranges-with-win32com":1997},{"id":4,"title":5,"body":6,"dateModified":1969,"datePublished":1969,"description":1970,"extension":1971,"faq":1972,"meta":1981,"navigation":207,"path":1990,"seo":1991,"slug":1993,"stem":1994,"type":1995,"__hash__":1996},"docs\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fread-and-write-cell-ranges-with-win32com\u002Findex.md","Read and Write Cell Ranges with win32com",{"type":7,"value":8,"toc":1954},"minimark",[9,19,142,147,172,179,303,307,312,421,428,518,522,541,638,641,645,726,736,872,883,887,906,1002,1012,1016,1019,1221,1227,1231,1234,1398,1409,1420,1424,1439,1519,1533,1537,1647,1651,1732,1735,1738,1853,1859,1863,1878,1882,1889,1895,1901,1907,1911,1950],[10,11,12,13,18],"p",{},"Everything a COM script does eventually comes down to moving values between Python and a worksheet,\nand the difference between a script that takes two seconds and one that takes four minutes is\nentirely in how those moves are batched. This guide, part of\n",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002F","Automating Excel with COM and pywin32",",\ncovers reading and writing blocks, the shape Excel expects, and the conversions that happen at the\nboundary.",[20,21,29,30,29,34,29,38,29,45,29,55,29,62,29,69,29,74,29,78,29,82,29,87,29,92,29,96,29,99,29,102,29,105,29,114,29,120,29,125,29,130,29,134,29,137],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"0 0 760 201","img",[25,26],"rng-shape-t","rng-shape-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"The shape Excel expects in each direction",[35,36,37],"desc",{"id":26},"Reading a multi-cell range returns a tuple of row tuples, and writing requires the same nesting — a flat list is interpreted as one row and fills only the first cell of a column.",[39,40],"rect",{"x":41,"y":41,"width":42,"height":43,"fill":44},"0","760","201","#ffffff",[39,46],{"x":47,"y":48,"width":49,"height":50,"rx":51,"fill":52,"stroke":53,"style":54},"20","28","270.0","139","14","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[56,57,61],"text",{"x":58,"y":59,"style":60},"155.0","54","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","reading",[63,64],"line",{"x1":65,"y1":66,"x2":67,"y2":66,"stroke":53,"style":68},"36","64","274.0","stroke-width:1px",[56,70,73],{"x":58,"y":71,"style":72},"86","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","multi-cell: tuple of tuples",[56,75,77],{"x":58,"y":76,"style":72},"109","single cell: a scalar",[56,79,81],{"x":58,"y":80,"style":72},"132","None for empty cells",[39,83],{"x":84,"y":48,"width":49,"height":50,"rx":51,"fill":85,"stroke":86,"style":54},"470.0","#fdefd8","var(--gold,#b4740a)",[56,88,91],{"x":89,"y":59,"style":90},"605.0","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","writing",[63,93],{"x1":94,"y1":66,"x2":95,"y2":66,"stroke":86,"style":68},"486.0","724.0",[56,97,98],{"x":89,"y":71,"style":72},"needs rows of values",[56,100,101],{"x":89,"y":76,"style":72},"[[v] for v in ...] is a column",[56,103,104],{"x":89,"y":80,"style":72},"[values] is a row",[39,106],{"x":107,"y":108,"width":109,"height":110,"rx":111,"fill":112,"stroke":113},"316.0","78.5","128","38","19","#ebebfd","var(--brand,#5b5cf0)",[56,115,119],{"x":116,"y":117,"style":118},"380.0","102.5","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Range.Value",[63,121],{"x1":122,"y1":123,"x2":124,"y2":123,"stroke":113,"style":54},"295.0","97.5","309.0",[126,127],"polygon",{"points":128,"fill":129},"309.0,97.5 300.0,92.5 300.0,102.5","#5b5cf0",[63,131],{"x1":132,"y1":123,"x2":133,"y2":123,"stroke":113,"style":54},"449.0","463.0",[126,135],{"points":136,"fill":129},"463.0,97.5 454.0,92.5 454.0,102.5",[56,138,141],{"x":116,"y":139,"style":140},"187","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","the nesting is the whole trick",[143,144,146],"h2",{"id":145},"prerequisites","Prerequisites",[148,149,154],"pre",{"className":150,"code":151,"language":152,"meta":153,"style":153},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pywin32\n","bash","",[155,156,157],"code",{"__ignoreMap":153},[158,159,161,165,169],"span",{"class":63,"line":160},1,[158,162,164],{"class":163},"sMTad","pip",[158,166,168],{"class":167},"srMev"," install",[158,170,171],{"class":167}," pywin32\n",[10,173,174,175,178],{},"The snippets below assume an open workbook and a ",[155,176,177],{},"sheet"," object obtained the usual way:",[148,180,184],{"className":181,"code":182,"language":183,"meta":153,"style":153},"language-python shiki shiki-themes github-light github-dark-high-contrast","import win32com.client as win32\n\nexcel = win32.DispatchEx(\"Excel.Application\")\nexcel.Visible = False\nexcel.DisplayAlerts = False\nbook = excel.Workbooks.Open(r\"C:\\data\\orders.xlsx\")\nsheet = book.Sheets(\"Data\")\n","python",[155,185,186,202,209,227,239,249,287],{"__ignoreMap":153},[158,187,188,192,196,199],{"class":63,"line":160},[158,189,191],{"class":190},"s-kum","import",[158,193,195],{"class":194},"skGVy"," win32com.client ",[158,197,198],{"class":190},"as",[158,200,201],{"class":194}," win32\n",[158,203,205],{"class":63,"line":204},2,[158,206,208],{"emptyLinePlaceholder":207},true,"\n",[158,210,212,215,218,221,224],{"class":63,"line":211},3,[158,213,214],{"class":194},"excel ",[158,216,217],{"class":190},"=",[158,219,220],{"class":194}," win32.DispatchEx(",[158,222,223],{"class":167},"\"Excel.Application\"",[158,225,226],{"class":194},")\n",[158,228,230,233,235],{"class":63,"line":229},4,[158,231,232],{"class":194},"excel.Visible ",[158,234,217],{"class":190},[158,236,238],{"class":237},"sP0c6"," False\n",[158,240,242,245,247],{"class":63,"line":241},5,[158,243,244],{"class":194},"excel.DisplayAlerts ",[158,246,217],{"class":190},[158,248,238],{"class":237},[158,250,252,255,257,260,263,266,269,272,276,279,282,285],{"class":63,"line":251},6,[158,253,254],{"class":194},"book ",[158,256,217],{"class":190},[158,258,259],{"class":194}," excel.Workbooks.Open(",[158,261,262],{"class":190},"r",[158,264,265],{"class":167},"\"C:",[158,267,268],{"class":237},"\\d",[158,270,271],{"class":167},"ata",[158,273,275],{"class":274},"s_b0D","\\o",[158,277,278],{"class":167},"rders",[158,280,281],{"class":237},".",[158,283,284],{"class":167},"xlsx\"",[158,286,226],{"class":194},[158,288,290,293,295,298,301],{"class":63,"line":289},7,[158,291,292],{"class":194},"sheet ",[158,294,217],{"class":190},[158,296,297],{"class":194}," book.Sheets(",[158,299,300],{"class":167},"\"Data\"",[158,302,226],{"class":194},[143,304,306],{"id":305},"reading-a-block-in-one-call","Reading a block in one call",[10,308,309,311],{},[155,310,119],{}," on a multi-cell range returns a tuple of row tuples — Excel's own array, converted\nonce at the boundary.",[148,313,315],{"className":181,"code":314,"language":183,"meta":153,"style":153},"rows = sheet.Range(\"A2:D5001\").Value\nprint(type(rows), len(rows), rows[0])\n# \u003Cclass 'tuple'> 5000 ('1001', 'A-100', 3, 19.99)\n\nheader = sheet.Range(\"A1:D1\").Value[0]\nframe_ready = [dict(zip(header, row)) for row in rows]\n",[155,316,317,333,358,364,368,388],{"__ignoreMap":153},[158,318,319,322,324,327,330],{"class":63,"line":160},[158,320,321],{"class":194},"rows ",[158,323,217],{"class":190},[158,325,326],{"class":194}," sheet.Range(",[158,328,329],{"class":167},"\"A2:D5001\"",[158,331,332],{"class":194},").Value\n",[158,334,335,338,341,344,347,350,353,355],{"class":63,"line":204},[158,336,337],{"class":237},"print",[158,339,340],{"class":194},"(",[158,342,343],{"class":237},"type",[158,345,346],{"class":194},"(rows), ",[158,348,349],{"class":237},"len",[158,351,352],{"class":194},"(rows), rows[",[158,354,41],{"class":237},[158,356,357],{"class":194},"])\n",[158,359,360],{"class":63,"line":211},[158,361,363],{"class":362},"s-wDw","# \u003Cclass 'tuple'> 5000 ('1001', 'A-100', 3, 19.99)\n",[158,365,366],{"class":63,"line":229},[158,367,208],{"emptyLinePlaceholder":207},[158,369,370,373,375,377,380,383,385],{"class":63,"line":241},[158,371,372],{"class":194},"header ",[158,374,217],{"class":190},[158,376,326],{"class":194},[158,378,379],{"class":167},"\"A1:D1\"",[158,381,382],{"class":194},").Value[",[158,384,41],{"class":237},[158,386,387],{"class":194},"]\n",[158,389,390,393,395,398,401,403,406,409,412,415,418],{"class":63,"line":251},[158,391,392],{"class":194},"frame_ready ",[158,394,217],{"class":190},[158,396,397],{"class":194}," [",[158,399,400],{"class":237},"dict",[158,402,340],{"class":194},[158,404,405],{"class":237},"zip",[158,407,408],{"class":194},"(header, row)) ",[158,410,411],{"class":190},"for",[158,413,414],{"class":194}," row ",[158,416,417],{"class":190},"in",[158,419,420],{"class":194}," rows]\n",[10,422,423,424,427],{},"A single cell is different: ",[155,425,426],{},"sheet.Range(\"B2\").Value"," returns the scalar, not a nested tuple. Code\nthat has to handle both shapes is code that will eventually be wrong, so it is worth normalising\nonce:",[148,429,431],{"className":181,"code":430,"language":183,"meta":153,"style":153},"def read_block(sheet, address):\n    value = sheet.Range(address).Value\n    if not isinstance(value, tuple):\n        return ((value,),)\n    if not isinstance(value[0], tuple):\n        return (value,)\n    return value\n",[155,432,433,445,455,475,483,503,510],{"__ignoreMap":153},[158,434,435,438,442],{"class":63,"line":160},[158,436,437],{"class":190},"def",[158,439,441],{"class":440},"s_Opv"," read_block",[158,443,444],{"class":194},"(sheet, address):\n",[158,446,447,450,452],{"class":63,"line":204},[158,448,449],{"class":194},"    value ",[158,451,217],{"class":190},[158,453,454],{"class":194}," sheet.Range(address).Value\n",[158,456,457,460,463,466,469,472],{"class":63,"line":211},[158,458,459],{"class":190},"    if",[158,461,462],{"class":190}," not",[158,464,465],{"class":237}," isinstance",[158,467,468],{"class":194},"(value, ",[158,470,471],{"class":237},"tuple",[158,473,474],{"class":194},"):\n",[158,476,477,480],{"class":63,"line":229},[158,478,479],{"class":190},"        return",[158,481,482],{"class":194}," ((value,),)\n",[158,484,485,487,489,491,494,496,499,501],{"class":63,"line":241},[158,486,459],{"class":190},[158,488,462],{"class":190},[158,490,465],{"class":237},[158,492,493],{"class":194},"(value[",[158,495,41],{"class":237},[158,497,498],{"class":194},"], ",[158,500,471],{"class":237},[158,502,474],{"class":194},[158,504,505,507],{"class":63,"line":251},[158,506,479],{"class":190},[158,508,509],{"class":194}," (value,)\n",[158,511,512,515],{"class":63,"line":289},[158,513,514],{"class":190},"    return",[158,516,517],{"class":194}," value\n",[143,519,521],{"id":520},"writing-a-block-in-one-call","Writing a block in one call",[10,523,524,525,528,529,532,533,536,537,540],{},"The write direction is stricter. Excel wants a sequence of row sequences, and a flat list is read as\na single row — which is why assigning ",[155,526,527],{},"[1, 2, 3]"," to ",[155,530,531],{},"A1:A3"," puts ",[155,534,535],{},"1"," in ",[155,538,539],{},"A1"," and nothing else.",[148,542,544],{"className":181,"code":543,"language":183,"meta":153,"style":153},"values = [12400.0, 9800.5, 15320.25]\n\nsheet.Range(\"B2:B4\").Value = [[v] for v in values]        # a column\nsheet.Range(\"B2:D2\").Value = [values]                     # a row\nsheet.Range(\"B2:D3\").Value = [values, values]             # a block\n",[155,545,546,571,575,604,621],{"__ignoreMap":153},[158,547,548,551,553,555,558,561,564,566,569],{"class":63,"line":160},[158,549,550],{"class":194},"values ",[158,552,217],{"class":190},[158,554,397],{"class":194},[158,556,557],{"class":237},"12400.0",[158,559,560],{"class":194},", ",[158,562,563],{"class":237},"9800.5",[158,565,560],{"class":194},[158,567,568],{"class":237},"15320.25",[158,570,387],{"class":194},[158,572,573],{"class":63,"line":204},[158,574,208],{"emptyLinePlaceholder":207},[158,576,577,580,583,586,588,591,593,596,598,601],{"class":63,"line":211},[158,578,579],{"class":194},"sheet.Range(",[158,581,582],{"class":167},"\"B2:B4\"",[158,584,585],{"class":194},").Value ",[158,587,217],{"class":190},[158,589,590],{"class":194}," [[v] ",[158,592,411],{"class":190},[158,594,595],{"class":194}," v ",[158,597,417],{"class":190},[158,599,600],{"class":194}," values]        ",[158,602,603],{"class":362},"# a column\n",[158,605,606,608,611,613,615,618],{"class":63,"line":229},[158,607,579],{"class":194},[158,609,610],{"class":167},"\"B2:D2\"",[158,612,585],{"class":194},[158,614,217],{"class":190},[158,616,617],{"class":194}," [values]                     ",[158,619,620],{"class":362},"# a row\n",[158,622,623,625,628,630,632,635],{"class":63,"line":241},[158,624,579],{"class":194},[158,626,627],{"class":167},"\"B2:D3\"",[158,629,585],{"class":194},[158,631,217],{"class":190},[158,633,634],{"class":194}," [values, values]             ",[158,636,637],{"class":362},"# a block\n",[10,639,640],{},"The range size and the data size have to agree. A block smaller than the range leaves the remainder\nuntouched; a block larger than the range is silently truncated, which is the more dangerous of the\ntwo because nothing complains.",[143,642,644],{"id":643},"finding-the-extent-of-the-data","Finding the extent of the data",[20,646,29,651,29,654,29,657,29,660,29,665,29,674,29,680,29,685,29,690,29,693,29,697,29,701,29,704,29,708,29,711,29,714,29,719,29,722],{"viewBox":647,"role":23,"ariaLabelledBy":648,"xmlns":27,"style":28},"0 0 760 232",[649,650],"rng-extent-t","rng-extent-d",[31,652,653],{"id":649},"Finding the real extent of the data",[35,655,656],{"id":650},"Starting from the bottom of the column and navigating up with the xlUp constant lands on the last populated row, which is more reliable than UsedRange after rows have been deleted.",[39,658],{"x":41,"y":41,"width":42,"height":659,"fill":44},"232",[56,661,664],{"x":116,"y":662,"style":663},"32","font-size:13px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","sizing a range",[39,666],{"x":667,"y":668,"width":669,"height":670,"rx":671,"fill":672,"stroke":673,"style":54},"24.0","74","208.0","96","12","#e7ebef","var(--line,#cdd5e6)",[56,675,679],{"x":676,"y":677,"style":678},"128.0","114","font-size:14px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","Rows.Count",[56,681,684],{"x":676,"y":682,"style":683},"136","font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","the bottom of the sheet",[63,686],{"x1":687,"y1":688,"x2":689,"y2":688,"stroke":113,"style":54},"237.0","122.0","269.0",[126,691],{"points":692,"fill":129},"269.0,122.0 260.0,117.0 260.0,127.0",[39,694],{"x":695,"y":668,"width":669,"height":670,"rx":671,"fill":696,"stroke":113,"style":54},"276.0","#f0f4ff",[56,698,700],{"x":116,"y":677,"style":699},"font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","End(xlUp)",[56,702,703],{"x":116,"y":682,"style":683},"jump to the last value",[63,705],{"x1":706,"y1":688,"x2":707,"y2":688,"stroke":113,"style":54},"489.0","521.0",[126,709],{"points":710,"fill":129},"521.0,122.0 512.0,117.0 512.0,127.0",[39,712],{"x":713,"y":668,"width":669,"height":670,"rx":671,"fill":52,"stroke":53,"style":54},"528.0",[56,715,718],{"x":716,"y":677,"style":717},"632.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","last_row",[56,720,721],{"x":716,"y":682,"style":683},"size the range from data",[56,723,725],{"x":116,"y":724,"style":140},"210","UsedRange can be stale; navigation from the bottom is not",[10,727,728,731,732,735],{},[155,729,730],{},"UsedRange"," is Excel's own answer and it is often too generous — deleting rows without clearing\ntheir formatting leaves them inside it. The ",[155,733,734],{},"End"," navigation that a person performs with Ctrl+Arrow\nis more reliable:",[148,737,739],{"className":181,"code":738,"language":183,"meta":153,"style":153},"XL_UP, XL_TO_LEFT = -4162, -4159\n\nlast_row = sheet.Cells(sheet.Rows.Count, 1).End(XL_UP).Row\nlast_col = sheet.Cells(1, sheet.Columns.Count).End(XL_TO_LEFT).Column\nprint(f\"data occupies {last_row} rows and {last_col} columns\")\n\nblock = sheet.Range(sheet.Cells(2, 1), sheet.Cells(last_row, last_col)).Value\n",[155,740,741,768,772,792,812,848,852],{"__ignoreMap":153},[158,742,743,746,748,751,754,757,760,762,765],{"class":63,"line":160},[158,744,745],{"class":237},"XL_UP",[158,747,560],{"class":194},[158,749,750],{"class":237},"XL_TO_LEFT",[158,752,753],{"class":190}," =",[158,755,756],{"class":190}," -",[158,758,759],{"class":237},"4162",[158,761,560],{"class":194},[158,763,764],{"class":190},"-",[158,766,767],{"class":237},"4159\n",[158,769,770],{"class":63,"line":204},[158,771,208],{"emptyLinePlaceholder":207},[158,773,774,777,779,782,784,787,789],{"class":63,"line":211},[158,775,776],{"class":194},"last_row ",[158,778,217],{"class":190},[158,780,781],{"class":194}," sheet.Cells(sheet.Rows.Count, ",[158,783,535],{"class":237},[158,785,786],{"class":194},").End(",[158,788,745],{"class":237},[158,790,791],{"class":194},").Row\n",[158,793,794,797,799,802,804,807,809],{"class":63,"line":229},[158,795,796],{"class":194},"last_col ",[158,798,217],{"class":190},[158,800,801],{"class":194}," sheet.Cells(",[158,803,535],{"class":237},[158,805,806],{"class":194},", sheet.Columns.Count).End(",[158,808,750],{"class":237},[158,810,811],{"class":194},").Column\n",[158,813,814,816,818,821,824,828,830,833,836,838,841,843,846],{"class":63,"line":241},[158,815,337],{"class":237},[158,817,340],{"class":194},[158,819,820],{"class":190},"f",[158,822,823],{"class":167},"\"data occupies ",[158,825,827],{"class":826},"sSjpA","{",[158,829,718],{"class":194},[158,831,832],{"class":826},"}",[158,834,835],{"class":167}," rows and ",[158,837,827],{"class":826},[158,839,840],{"class":194},"last_col",[158,842,832],{"class":826},[158,844,845],{"class":167}," columns\"",[158,847,226],{"class":194},[158,849,850],{"class":63,"line":251},[158,851,208],{"emptyLinePlaceholder":207},[158,853,854,857,859,862,865,867,869],{"class":63,"line":289},[158,855,856],{"class":194},"block ",[158,858,217],{"class":190},[158,860,861],{"class":194}," sheet.Range(sheet.Cells(",[158,863,864],{"class":237},"2",[158,866,560],{"class":194},[158,868,535],{"class":237},[158,870,871],{"class":194},"), sheet.Cells(last_row, last_col)).Value\n",[10,873,874,875,878,879,882],{},"Building a range from two ",[155,876,877],{},"Cells"," calls is the programmatic equivalent of ",[155,880,881],{},"A2:D5001",", and it avoids\nconstructing address strings with column letters — worth it as soon as the column count is dynamic.",[143,884,886],{"id":885},"types-at-the-boundary","Types at the boundary",[10,888,889,890,893,894,897,898,901,902,905],{},"Conversions happen automatically and mostly do what you want. Numbers arrive as ",[155,891,892],{},"float"," even when\nthe cell shows an integer, dates arrive as ",[155,895,896],{},"datetime"," objects with a timezone attached by pywin32,\nempty cells arrive as ",[155,899,900],{},"None",", and error cells arrive as integers that look nothing like the ",[155,903,904],{},"#N\u002FA","\non screen.",[148,907,909],{"className":181,"code":908,"language":183,"meta":153,"style":153},"from datetime import datetime\n\nraw = sheet.Range(\"A2:C3\").Value2      # Value2 skips date and currency conversion\nconverted = sheet.Range(\"A2:C3\").Value\n\nprint(raw[0][0])        # 45658.0  — the Excel serial number\nprint(converted[0][0])  # datetime object\n",[155,910,911,924,928,946,959,963,983],{"__ignoreMap":153},[158,912,913,916,919,921],{"class":63,"line":160},[158,914,915],{"class":190},"from",[158,917,918],{"class":194}," datetime ",[158,920,191],{"class":190},[158,922,923],{"class":194}," datetime\n",[158,925,926],{"class":63,"line":204},[158,927,208],{"emptyLinePlaceholder":207},[158,929,930,933,935,937,940,943],{"class":63,"line":211},[158,931,932],{"class":194},"raw ",[158,934,217],{"class":190},[158,936,326],{"class":194},[158,938,939],{"class":167},"\"A2:C3\"",[158,941,942],{"class":194},").Value2      ",[158,944,945],{"class":362},"# Value2 skips date and currency conversion\n",[158,947,948,951,953,955,957],{"class":63,"line":229},[158,949,950],{"class":194},"converted ",[158,952,217],{"class":190},[158,954,326],{"class":194},[158,956,939],{"class":167},[158,958,332],{"class":194},[158,960,961],{"class":63,"line":241},[158,962,208],{"emptyLinePlaceholder":207},[158,964,965,967,970,972,975,977,980],{"class":63,"line":251},[158,966,337],{"class":237},[158,968,969],{"class":194},"(raw[",[158,971,41],{"class":237},[158,973,974],{"class":194},"][",[158,976,41],{"class":237},[158,978,979],{"class":194},"])        ",[158,981,982],{"class":362},"# 45658.0  — the Excel serial number\n",[158,984,985,987,990,992,994,996,999],{"class":63,"line":289},[158,986,337],{"class":237},[158,988,989],{"class":194},"(converted[",[158,991,41],{"class":237},[158,993,974],{"class":194},[158,995,41],{"class":237},[158,997,998],{"class":194},"])  ",[158,1000,1001],{"class":362},"# datetime object\n",[10,1003,1004,1007,1008,281],{},[155,1005,1006],{},"Value2"," is worth knowing about on large blocks: skipping the date conversion for tens of thousands\nof cells is a measurable saving, and converting serials yourself is a two-line job covered in\n",[14,1009,1011],{"href":1010},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffix-excel-serial-numbers-showing-instead-of-dates\u002F","Fix Excel Serial Numbers Showing Instead of Dates",[143,1013,1015],{"id":1014},"into-and-out-of-a-dataframe","Into and out of a DataFrame",[10,1017,1018],{},"pywin32 has no DataFrame support of its own, but the conversion is three lines in each direction and\nlets the rest of a pipeline use the tools it already has.",[148,1020,1022],{"className":181,"code":1021,"language":183,"meta":153,"style":153},"import pandas as pd\n\nheader = sheet.Range(\"A1:D1\").Value[0]\nbody = sheet.Range(sheet.Cells(2, 1), sheet.Cells(last_row, 4)).Value\nframe = pd.DataFrame(list(body), columns=list(header))\n\nsummary = frame.groupby(\"Region\", as_index=False)[\"Revenue\"].sum()\n\ntarget = book.Sheets(\"Summary\")\ntarget.Range(\"A1:B1\").Value = [list(summary.columns)]\ntarget.Range(target.Cells(2, 1), target.Cells(len(summary) + 1, 2)).Value = summary.values.tolist()\n",[155,1023,1024,1036,1040,1056,1080,1107,1111,1143,1148,1163,1183],{"__ignoreMap":153},[158,1025,1026,1028,1031,1033],{"class":63,"line":160},[158,1027,191],{"class":190},[158,1029,1030],{"class":194}," pandas ",[158,1032,198],{"class":190},[158,1034,1035],{"class":194}," pd\n",[158,1037,1038],{"class":63,"line":204},[158,1039,208],{"emptyLinePlaceholder":207},[158,1041,1042,1044,1046,1048,1050,1052,1054],{"class":63,"line":211},[158,1043,372],{"class":194},[158,1045,217],{"class":190},[158,1047,326],{"class":194},[158,1049,379],{"class":167},[158,1051,382],{"class":194},[158,1053,41],{"class":237},[158,1055,387],{"class":194},[158,1057,1058,1061,1063,1065,1067,1069,1071,1074,1077],{"class":63,"line":229},[158,1059,1060],{"class":194},"body ",[158,1062,217],{"class":190},[158,1064,861],{"class":194},[158,1066,864],{"class":237},[158,1068,560],{"class":194},[158,1070,535],{"class":237},[158,1072,1073],{"class":194},"), sheet.Cells(last_row, ",[158,1075,1076],{"class":237},"4",[158,1078,1079],{"class":194},")).Value\n",[158,1081,1082,1085,1087,1090,1093,1096,1100,1102,1104],{"class":63,"line":241},[158,1083,1084],{"class":194},"frame ",[158,1086,217],{"class":190},[158,1088,1089],{"class":194}," pd.DataFrame(",[158,1091,1092],{"class":237},"list",[158,1094,1095],{"class":194},"(body), ",[158,1097,1099],{"class":1098},"sa561","columns",[158,1101,217],{"class":190},[158,1103,1092],{"class":237},[158,1105,1106],{"class":194},"(header))\n",[158,1108,1109],{"class":63,"line":251},[158,1110,208],{"emptyLinePlaceholder":207},[158,1112,1113,1116,1118,1121,1124,1126,1129,1131,1134,1137,1140],{"class":63,"line":289},[158,1114,1115],{"class":194},"summary ",[158,1117,217],{"class":190},[158,1119,1120],{"class":194}," frame.groupby(",[158,1122,1123],{"class":167},"\"Region\"",[158,1125,560],{"class":194},[158,1127,1128],{"class":1098},"as_index",[158,1130,217],{"class":190},[158,1132,1133],{"class":237},"False",[158,1135,1136],{"class":194},")[",[158,1138,1139],{"class":167},"\"Revenue\"",[158,1141,1142],{"class":194},"].sum()\n",[158,1144,1146],{"class":63,"line":1145},8,[158,1147,208],{"emptyLinePlaceholder":207},[158,1149,1151,1154,1156,1158,1161],{"class":63,"line":1150},9,[158,1152,1153],{"class":194},"target ",[158,1155,217],{"class":190},[158,1157,297],{"class":194},[158,1159,1160],{"class":167},"\"Summary\"",[158,1162,226],{"class":194},[158,1164,1166,1169,1172,1174,1176,1178,1180],{"class":63,"line":1165},10,[158,1167,1168],{"class":194},"target.Range(",[158,1170,1171],{"class":167},"\"A1:B1\"",[158,1173,585],{"class":194},[158,1175,217],{"class":190},[158,1177,397],{"class":194},[158,1179,1092],{"class":237},[158,1181,1182],{"class":194},"(summary.columns)]\n",[158,1184,1186,1189,1191,1193,1195,1198,1200,1203,1206,1209,1211,1213,1216,1218],{"class":63,"line":1185},11,[158,1187,1188],{"class":194},"target.Range(target.Cells(",[158,1190,864],{"class":237},[158,1192,560],{"class":194},[158,1194,535],{"class":237},[158,1196,1197],{"class":194},"), target.Cells(",[158,1199,349],{"class":237},[158,1201,1202],{"class":194},"(summary) ",[158,1204,1205],{"class":190},"+",[158,1207,1208],{"class":237}," 1",[158,1210,560],{"class":194},[158,1212,864],{"class":237},[158,1214,1215],{"class":194},")).Value ",[158,1217,217],{"class":190},[158,1219,1220],{"class":194}," summary.values.tolist()\n",[10,1222,1223,1226],{},[155,1224,1225],{},"summary.values.tolist()"," is the important detail: NumPy scalar types do not marshal across COM, and\npassing the array directly raises a type error that names neither NumPy nor Excel clearly.",[143,1228,1230],{"id":1229},"formatting-a-range-without-looping","Formatting a range without looping",[10,1232,1233],{},"Formatting is subject to exactly the same call-cost rule as values, and it is where scripts most\noften regress into a per-cell loop. Almost every visual property is settable on a whole range at\nonce, which means a formatted report costs a handful of calls rather than one per cell.",[148,1235,1237],{"className":181,"code":1236,"language":183,"meta":153,"style":153},"XL_CENTER, XL_EDGE_BOTTOM, XL_THIN = -4108, 9, 2\n\nheader = sheet.Range(\"A1:D1\")\nheader.Font.Bold = True\nheader.Font.Size = 11\nheader.Interior.Color = 0xF0F0F0            # BGR, not RGB\nheader.HorizontalAlignment = XL_CENTER\nheader.Borders(XL_EDGE_BOTTOM).Weight = XL_THIN\n\nsheet.Range(\"D2:D5001\").NumberFormat = \"#,##0.00\"\nsheet.Range(\"A:D\").EntireColumn.AutoFit()\nsheet.Range(\"A2\").Select()\nexcel.ActiveWindow.FreezePanes = True\n",[155,1238,1239,1271,1275,1287,1297,1307,1323,1333,1348,1352,1367,1377,1388],{"__ignoreMap":153},[158,1240,1241,1244,1246,1249,1251,1254,1256,1258,1261,1263,1266,1268],{"class":63,"line":160},[158,1242,1243],{"class":237},"XL_CENTER",[158,1245,560],{"class":194},[158,1247,1248],{"class":237},"XL_EDGE_BOTTOM",[158,1250,560],{"class":194},[158,1252,1253],{"class":237},"XL_THIN",[158,1255,753],{"class":190},[158,1257,756],{"class":190},[158,1259,1260],{"class":237},"4108",[158,1262,560],{"class":194},[158,1264,1265],{"class":237},"9",[158,1267,560],{"class":194},[158,1269,1270],{"class":237},"2\n",[158,1272,1273],{"class":63,"line":204},[158,1274,208],{"emptyLinePlaceholder":207},[158,1276,1277,1279,1281,1283,1285],{"class":63,"line":211},[158,1278,372],{"class":194},[158,1280,217],{"class":190},[158,1282,326],{"class":194},[158,1284,379],{"class":167},[158,1286,226],{"class":194},[158,1288,1289,1292,1294],{"class":63,"line":229},[158,1290,1291],{"class":194},"header.Font.Bold ",[158,1293,217],{"class":190},[158,1295,1296],{"class":237}," True\n",[158,1298,1299,1302,1304],{"class":63,"line":241},[158,1300,1301],{"class":194},"header.Font.Size ",[158,1303,217],{"class":190},[158,1305,1306],{"class":237}," 11\n",[158,1308,1309,1312,1314,1317,1320],{"class":63,"line":251},[158,1310,1311],{"class":194},"header.Interior.Color ",[158,1313,217],{"class":190},[158,1315,1316],{"class":190}," 0x",[158,1318,1319],{"class":237},"F0F0F0",[158,1321,1322],{"class":362},"            # BGR, not RGB\n",[158,1324,1325,1328,1330],{"class":63,"line":289},[158,1326,1327],{"class":194},"header.HorizontalAlignment ",[158,1329,217],{"class":190},[158,1331,1332],{"class":237}," XL_CENTER\n",[158,1334,1335,1338,1340,1343,1345],{"class":63,"line":1145},[158,1336,1337],{"class":194},"header.Borders(",[158,1339,1248],{"class":237},[158,1341,1342],{"class":194},").Weight ",[158,1344,217],{"class":190},[158,1346,1347],{"class":237}," XL_THIN\n",[158,1349,1350],{"class":63,"line":1150},[158,1351,208],{"emptyLinePlaceholder":207},[158,1353,1354,1356,1359,1362,1364],{"class":63,"line":1165},[158,1355,579],{"class":194},[158,1357,1358],{"class":167},"\"D2:D5001\"",[158,1360,1361],{"class":194},").NumberFormat ",[158,1363,217],{"class":190},[158,1365,1366],{"class":167}," \"#,##0.00\"\n",[158,1368,1369,1371,1374],{"class":63,"line":1185},[158,1370,579],{"class":194},[158,1372,1373],{"class":167},"\"A:D\"",[158,1375,1376],{"class":194},").EntireColumn.AutoFit()\n",[158,1378,1380,1382,1385],{"class":63,"line":1379},12,[158,1381,579],{"class":194},[158,1383,1384],{"class":167},"\"A2\"",[158,1386,1387],{"class":194},").Select()\n",[158,1389,1391,1394,1396],{"class":63,"line":1390},13,[158,1392,1393],{"class":194},"excel.ActiveWindow.FreezePanes ",[158,1395,217],{"class":190},[158,1397,1296],{"class":237},[10,1399,1400,1401,1404,1405,1408],{},"The colour ordering catches everyone once: Excel's COM interface takes colours as BGR integers, so\n",[155,1402,1403],{},"0xFF0000"," is blue rather than red. Writing them as ",[155,1406,1407],{},"red | (green \u003C\u003C 8) | (blue \u003C\u003C 16)"," makes the\nintent explicit and stops the next reader from assuming a typo.",[10,1410,1411,1414,1415,1419],{},[155,1412,1413],{},"EntireColumn.AutoFit()"," is one of the few things genuinely easier here than in the file-level\nlibraries, which have to measure text themselves — the approach described in\n",[14,1416,1418],{"href":1417},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fauto-fit-column-widths-when-writing-with-pandas\u002F","Auto-Fit Column Widths When Writing with Pandas",".\nExcel knows its own font metrics, so it gets the answer right where an estimate does not.",[143,1421,1423],{"id":1422},"clearing-and-inserting-without-corrupting-the-sheet","Clearing and inserting without corrupting the sheet",[10,1425,1426,1427,1430,1431,1434,1435,1438],{},"Two operations deserve care because they move data that other formulas point at. ",[155,1428,1429],{},"ClearContents","\nremoves values but leaves formatting; ",[155,1432,1433],{},"Clear"," removes both; ",[155,1436,1437],{},"Delete"," removes the cells themselves\nand shifts everything below or to the right, which is what breaks references.",[148,1440,1442],{"className":181,"code":1441,"language":183,"meta":153,"style":153},"XL_SHIFT_UP, XL_SHIFT_DOWN = -4162, -4121\n\nsheet.Range(\"A2:D5001\").ClearContents()          # keep the formatting, drop the data\nsheet.Range(\"A2:A11\").Insert(XL_SHIFT_DOWN)      # push existing rows down\nsheet.Range(\"10:12\").Delete(XL_SHIFT_UP)         # remove three whole rows\n",[155,1443,1444,1467,1471,1483,1501],{"__ignoreMap":153},[158,1445,1446,1449,1451,1454,1456,1458,1460,1462,1464],{"class":63,"line":160},[158,1447,1448],{"class":237},"XL_SHIFT_UP",[158,1450,560],{"class":194},[158,1452,1453],{"class":237},"XL_SHIFT_DOWN",[158,1455,753],{"class":190},[158,1457,756],{"class":190},[158,1459,759],{"class":237},[158,1461,560],{"class":194},[158,1463,764],{"class":190},[158,1465,1466],{"class":237},"4121\n",[158,1468,1469],{"class":63,"line":204},[158,1470,208],{"emptyLinePlaceholder":207},[158,1472,1473,1475,1477,1480],{"class":63,"line":211},[158,1474,579],{"class":194},[158,1476,329],{"class":167},[158,1478,1479],{"class":194},").ClearContents()          ",[158,1481,1482],{"class":362},"# keep the formatting, drop the data\n",[158,1484,1485,1487,1490,1493,1495,1498],{"class":63,"line":229},[158,1486,579],{"class":194},[158,1488,1489],{"class":167},"\"A2:A11\"",[158,1491,1492],{"class":194},").Insert(",[158,1494,1453],{"class":237},[158,1496,1497],{"class":194},")      ",[158,1499,1500],{"class":362},"# push existing rows down\n",[158,1502,1503,1505,1508,1511,1513,1516],{"class":63,"line":241},[158,1504,579],{"class":194},[158,1506,1507],{"class":167},"\"10:12\"",[158,1509,1510],{"class":194},").Delete(",[158,1512,1448],{"class":237},[158,1514,1515],{"class":194},")         ",[158,1517,1518],{"class":362},"# remove three whole rows\n",[10,1520,1521,1522,1524,1525,1528,1529,281],{},"For a report that is regenerated each month, ",[155,1523,1429],{}," on the data block followed by a single\nrange write is the safest pattern: the formatting, the header, the named ranges and any charts\npointing at the sheet all survive, and only the numbers change. Deleting rows to make room is what\nturns a chart's source range into ",[155,1526,1527],{},"#REF!"," — the same hazard covered from the openpyxl side in\n",[14,1530,1532],{"href":1531},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Finsert-and-delete-rows-and-columns-with-openpyxl\u002F","Insert and Delete Rows and Columns with openpyxl",[143,1534,1536],{"id":1535},"common-pitfalls","Common pitfalls",[1538,1539,1540,1556],"table",{},[1541,1542,1543],"thead",{},[1544,1545,1546,1550,1553],"tr",{},[1547,1548,1549],"th",{},"Symptom",[1547,1551,1552],{},"Cause",[1547,1554,1555],{},"Fix",[1557,1558,1559,1574,1592,1606,1622,1636],"tbody",{},[1544,1560,1561,1565,1568],{},[1562,1563,1564],"td",{},"Only the first cell of a column is filled",[1562,1566,1567],{},"A flat list was assigned",[1562,1569,1570,1571],{},"Wrap each value: ",[155,1572,1573],{},"[[v] for v in values]",[1544,1575,1576,1582,1585],{},[1562,1577,1578,1581],{},[155,1579,1580],{},"TypeError"," on a NumPy array",[1562,1583,1584],{},"NumPy scalars do not marshal over COM",[1562,1586,1587,1588,1591],{},"Convert with ",[155,1589,1590],{},".tolist()"," first",[1544,1593,1594,1597,1603],{},[1562,1595,1596],{},"Reading a single cell breaks the loop",[1562,1598,1599,1602],{},[155,1600,1601],{},"Value"," returns a scalar, not a tuple",[1562,1604,1605],{},"Normalise the shape, or always read two or more cells",[1544,1607,1608,1613,1616],{},[1562,1609,1610,1612],{},[155,1611,730],{}," includes thousands of empty rows",[1562,1614,1615],{},"Formatting left behind by deleted rows",[1562,1617,1618,1619,1621],{},"Use ",[155,1620,700],{}," from the bottom instead",[1544,1623,1624,1627,1630],{},[1562,1625,1626],{},"Dates are off by a day",[1562,1628,1629],{},"The 1900\u002F1904 date system, or a timezone-aware conversion",[1562,1631,1632,1633,1635],{},"Read with ",[155,1634,1006],{}," and convert the serial yourself",[1544,1637,1638,1641,1644],{},[1562,1639,1640],{},"The write is silently truncated",[1562,1642,1643],{},"The data block is larger than the target range",[1562,1645,1646],{},"Size the range from the data, not the other way round",[143,1648,1650],{"id":1649},"performance-and-scale","Performance and scale",[20,1652,29,1658,29,1661,29,1664,29,1668,29,1673,29,1679,29,1687,29,1693,29,1697,29,1700,29,1704,29,1709,29,1713,29,1715,29,1718,29,1723,29,1727],{"viewBox":1653,"role":23,"ariaLabelledBy":1654,"xmlns":27,"style":1657},"0 0 720 196",[1655,1656],"rng-cost-t","rng-cost-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1659,1660],{"id":1655},"Time to move 50,000 cells, by number of COM calls",[35,1662,1663],{"id":1656},"Reading cells one at a time makes fifty thousand crossings and takes minutes, reading a row at a time takes seconds, and reading the whole block in one call finishes in well under a second.",[39,1665],{"x":41,"y":41,"width":1666,"height":1667,"fill":44},"720","196",[56,1669,1672],{"x":47,"y":1670,"style":1671},"56","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:start","cell at a time",[39,1674],{"x":1675,"y":1676,"width":116,"height":1677,"rx":1678,"fill":672,"stroke":673},"200","40","26","6",[39,1680],{"x":43,"y":1681,"width":1682,"height":1683,"rx":1684,"fill":1685,"stroke":1686},"41","378.0","24","5","#fee8f2","var(--accent,#d81b73)",[56,1688,1692],{"x":1689,"y":1690,"style":1691},"592.0","58","font-size:12px;font-weight:700;fill:var(--accent,#d81b73);text-anchor:start","50,000 calls",[56,1694,1696],{"x":47,"y":1695,"style":1671},"100","row at a time",[39,1698],{"x":1675,"y":1699,"width":116,"height":1677,"rx":1678,"fill":672,"stroke":673},"84",[39,1701],{"x":43,"y":1702,"width":1703,"height":1683,"rx":1684,"fill":85,"stroke":86},"85","30.5",[56,1705,1708],{"x":1689,"y":1706,"style":1707},"102","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","5,000 calls",[56,1710,1712],{"x":47,"y":1711,"style":1671},"144","one block read",[39,1714],{"x":1675,"y":109,"width":116,"height":1677,"rx":1678,"fill":672,"stroke":673},[39,1716],{"x":43,"y":1717,"width":48,"height":1683,"rx":1684,"fill":52,"stroke":53},"129",[56,1719,1722],{"x":1689,"y":1720,"style":1721},"146","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","1 call",[56,1724,1726],{"x":47,"y":47,"style":1725},"font-size:11.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:start","relative cost",[56,1728,1731],{"x":1729,"y":1730,"style":140},"360.0","186","the data volume is identical in all three",[10,1733,1734],{},"One rule dominates: minimise crossings. A read of 50,000 cells in one call typically takes under a\nsecond; the same cells fetched individually take minutes. When a script must touch cells\nselectively, read the whole block once, do the selection in Python, and write back the whole block —\neven though that sounds wasteful, it is dramatically faster than the targeted alternative.",[10,1736,1737],{},"Two application settings help when writing large blocks into a sheet with formulas:",[148,1739,1741],{"className":181,"code":1740,"language":183,"meta":153,"style":153},"XL_MANUAL, XL_AUTOMATIC = -4135, -4105\n\nexcel.ScreenUpdating = False\nexcel.Calculation = XL_MANUAL\ntry:\n    sheet.Range(sheet.Cells(2, 1), sheet.Cells(50001, 8)).Value = big_block\nfinally:\n    excel.Calculation = XL_AUTOMATIC\n    excel.ScreenUpdating = True\n",[155,1742,1743,1767,1771,1780,1790,1798,1827,1834,1844],{"__ignoreMap":153},[158,1744,1745,1748,1750,1753,1755,1757,1760,1762,1764],{"class":63,"line":160},[158,1746,1747],{"class":237},"XL_MANUAL",[158,1749,560],{"class":194},[158,1751,1752],{"class":237},"XL_AUTOMATIC",[158,1754,753],{"class":190},[158,1756,756],{"class":190},[158,1758,1759],{"class":237},"4135",[158,1761,560],{"class":194},[158,1763,764],{"class":190},[158,1765,1766],{"class":237},"4105\n",[158,1768,1769],{"class":63,"line":204},[158,1770,208],{"emptyLinePlaceholder":207},[158,1772,1773,1776,1778],{"class":63,"line":211},[158,1774,1775],{"class":194},"excel.ScreenUpdating ",[158,1777,217],{"class":190},[158,1779,238],{"class":237},[158,1781,1782,1785,1787],{"class":63,"line":229},[158,1783,1784],{"class":194},"excel.Calculation ",[158,1786,217],{"class":190},[158,1788,1789],{"class":237}," XL_MANUAL\n",[158,1791,1792,1795],{"class":63,"line":241},[158,1793,1794],{"class":190},"try",[158,1796,1797],{"class":194},":\n",[158,1799,1800,1803,1805,1807,1809,1812,1815,1817,1820,1822,1824],{"class":63,"line":251},[158,1801,1802],{"class":194},"    sheet.Range(sheet.Cells(",[158,1804,864],{"class":237},[158,1806,560],{"class":194},[158,1808,535],{"class":237},[158,1810,1811],{"class":194},"), sheet.Cells(",[158,1813,1814],{"class":237},"50001",[158,1816,560],{"class":194},[158,1818,1819],{"class":237},"8",[158,1821,1215],{"class":194},[158,1823,217],{"class":190},[158,1825,1826],{"class":194}," big_block\n",[158,1828,1829,1832],{"class":63,"line":289},[158,1830,1831],{"class":190},"finally",[158,1833,1797],{"class":194},[158,1835,1836,1839,1841],{"class":63,"line":1145},[158,1837,1838],{"class":194},"    excel.Calculation ",[158,1840,217],{"class":190},[158,1842,1843],{"class":237}," XL_AUTOMATIC\n",[158,1845,1846,1849,1851],{"class":63,"line":1150},[158,1847,1848],{"class":194},"    excel.ScreenUpdating ",[158,1850,217],{"class":190},[158,1852,1296],{"class":237},[10,1854,1855,1856,1858],{},"Without them, Excel recalculates and repaints after every assignment. Restoring both in a ",[155,1857,1831],{},"\nmatters — leaving calculation on manual in an application the user later attaches to is a bug that\nwill be reported as \"my spreadsheet stopped working\".",[143,1860,1862],{"id":1861},"conclusion","Conclusion",[10,1864,1865,1866,1868,1869,1871,1872,1874,1875,1877],{},"Treat every property access as an expensive network call and the performance problem disappears.\nRead blocks with ",[155,1867,119],{},", write them as sequences of row sequences, size ranges from the data\nusing ",[155,1870,700],{}," rather than trusting ",[155,1873,730],{},", and convert to and from a DataFrame at the edges\nwith ",[155,1876,1590],{},". With screen updating and calculation suspended around a bulk write, a COM script\nhandles tens of thousands of rows comfortably.",[143,1879,1881],{"id":1880},"frequently-asked-questions","Frequently asked questions",[10,1883,1884,1888],{},[1885,1886,1887],"strong",{},"What does Range.Value return for a single cell?","\nA scalar — a float, string, bool or datetime — not a one-element tuple. For a multi-cell range it returns a tuple of row tuples. Code that handles both has to check, which is why it is usually simpler to always read a range of at least two cells.",[10,1890,1891,1894],{},[1885,1892,1893],{},"Why does assigning a flat list only fill the first cell?","\nExcel expects a two-dimensional structure. A flat list is interpreted as a single row, so assigning it to a column range fills one cell. Wrap each value in its own list to make it a column.",[10,1896,1897,1900],{},[1885,1898,1899],{},"What is the difference between Value and Value2?","\nValue2 returns dates and currency as raw numbers rather than converting them to Python datetimes and floats, which makes it measurably faster on large blocks. Use Value2 when you plan to convert the serials yourself.",[10,1902,1903,1906],{},[1885,1904,1905],{},"How do I find the used range reliably?","\nsheet.UsedRange gives Excel's own idea of it, which can be too large if rows were deleted without clearing formats. For a dependable last row, use sheet.Cells(sheet.Rows.Count, 1).End(-4162).Row — the xlUp constant.",[143,1908,1910],{"id":1909},"related","Related",[1912,1913,1914,1921,1928,1935,1942],"ul",{},[1915,1916,1917,1918,1920],"li",{},"Up one level: ",[14,1919,17],{"href":16}," — the session skeleton and the call-cost model.",[1915,1922,1923,1927],{},[14,1924,1926],{"href":1925},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frun-an-excel-macro-from-python-with-win32com\u002F","Run an Excel Macro from Python with win32com"," — staging a block, then letting VBA loop over it.",[1915,1929,1930,1934],{},[14,1931,1933],{"href":1932},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings\u002F","Read and Write a Live Excel Workbook with xlwings"," — the same operations with DataFrame conversion built in.",[1915,1936,1937,1941],{},[14,1938,1940],{"href":1939},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fiterate-over-rows-and-columns-with-openpyxl\u002F","Iterate Over Rows and Columns with openpyxl"," — the file-level equivalent, with no boundary to cross.",[1915,1943,1944,1946,1947,1949],{},[14,1945,1011],{"href":1010}," — converting what ",[155,1948,1006],{}," hands back.",[1951,1952,1953],"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 .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":153,"searchDepth":204,"depth":204,"links":1955},[1956,1957,1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968],{"id":145,"depth":204,"text":146},{"id":305,"depth":204,"text":306},{"id":520,"depth":204,"text":521},{"id":643,"depth":204,"text":644},{"id":885,"depth":204,"text":886},{"id":1014,"depth":204,"text":1015},{"id":1229,"depth":204,"text":1230},{"id":1422,"depth":204,"text":1423},{"id":1535,"depth":204,"text":1536},{"id":1649,"depth":204,"text":1650},{"id":1861,"depth":204,"text":1862},{"id":1880,"depth":204,"text":1881},{"id":1909,"depth":204,"text":1910},"2026-09-04","Move data between Python and Excel in single calls: block reads, the nested shape writes require, sizing ranges with End(xlUp), and Value versus Value2.","md",[1973,1975,1977,1979],{"q":1887,"a":1974},"A scalar — a float, string, bool or datetime — not a one-element tuple. For a multi-cell range it returns a tuple of row tuples. Code that handles both has to check, which is why it is usually simpler to always read a range of at least two cells.",{"q":1893,"a":1976},"Excel expects a two-dimensional structure. A flat list is interpreted as a single row, so assigning it to a column range fills one cell. Wrap each value in its own list to make it a column.",{"q":1899,"a":1978},"Value2 returns dates and currency as raw numbers rather than converting them to Python datetimes and floats, which makes it measurably faster on large blocks. Use Value2 when you plan to convert the serials yourself.",{"q":1905,"a":1980},"sheet.UsedRange gives Excel's own idea of it, which can be too large if rows were deleted without clearing formats. For a dependable last row, use sheet.Cells(sheet.Rows.Count, 1).End(-4162).Row — the xlUp constant.",{"breadcrumb":1982},[1983,1986,1989],{"name":1984,"item":1985},"Home","\u002F",{"name":1987,"item":1988},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fread-and-write-cell-ranges-with-win32com",{"title":5,"description":1992},"Batch COM range reads and writes in Python: tuple-of-tuples reads, nested-list writes, End(xlUp) sizing, Value2 for speed, and DataFrame conversion at the edges.","read-and-write-cell-ranges-with-win32com","getting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fread-and-write-cell-ranges-with-win32com\u002Findex","how-to","4JRL4c-bSCHyVy3PwDplmFftwyrFo5SgU3KzN-tDuSs",[1998,2002],{"title":1999,"path":2000,"stem":2001,"children":-1},"Handle COM Errors and Excel Dialog Prompts in Python","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fhandle-com-errors-and-excel-dialog-prompts-in-python","getting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fhandle-com-errors-and-excel-dialog-prompts-in-python\u002Findex",{"title":2003,"path":2004,"stem":2005,"children":-1},"Refresh Excel Data Connections and Recalculate with win32com","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com","getting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com\u002Findex",1788710154301]