[{"data":1,"prerenderedAt":2387},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl":2378},{"id":4,"title":5,"body":6,"dateModified":2348,"datePublished":2348,"description":2349,"extension":2350,"faq":2351,"meta":2360,"navigation":258,"path":2371,"seo":2372,"slug":2374,"stem":2375,"type":2376,"__hash__":2377},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl\u002Findex.md","Create a Named Range in Excel with openpyxl",{"type":7,"value":8,"toc":2334},"minimark",[9,36,163,168,192,210,214,217,646,652,655,659,670,952,969,1069,1076,1080,1087,1325,1336,1340,1343,1529,1539,1546,1550,1673,1755,1758,1762,1765,2095,2098,2102,2206,2210,2218,2222,2233,2237,2248,2258,2272,2282,2286,2289,2298,2301,2330],[10,11,12,13,17,18,21,22,25,26,29,30,35],"p",{},"A defined name turns ",[14,15,16],"code",{},"'Lookups'!$A$2:$A$40"," into ",[14,19,20],{},"Regions",". Formulas become readable, ranges can be repointed in one place, and dropdown validation stops breaking every time someone inserts a row. ",[14,23,24],{},"openpyxl"," creates them through the ",[14,27,28],{},"DefinedName"," class, and the two things worth understanding are the reference syntax and the scope. This guide, part of ",[31,32,34],"a",{"href":33},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python",", covers both, plus listing, updating and deleting names from Python.",[37,38,46,47,46,51,46,55,46,62,46,72,46,78,46,83,46,88,46,93,46,101,46,105,46,109,46,117,46,122,46,127,46,131,46,136,46,141,46,144,46,147,46,152,46,157,46,160],"svg",{"viewBox":39,"role":40,"ariaLabelledBy":41,"xmlns":44,"style":45},"0 0 740 214","img",[42,43],"nr-parts-t","nr-parts-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  ",[48,49,50],"title",{"id":42},"The parts of a defined name",[52,53,54],"desc",{"id":43},"A defined name has three parts: the name itself, which must contain no spaces; the sheet it points at, quoted when the sheet name contains a space; and the absolute cell range with dollar signs on the column and the row.",[56,57],"rect",{"x":58,"y":58,"width":59,"height":60,"fill":61},"0","740","214","#ffffff",[56,63],{"x":64,"y":65,"width":66,"height":67,"rx":68,"fill":69,"stroke":70,"style":71},"60","24","620","50","12","#ebebfd","var(--brand,#5b5cf0)","stroke-width:2px",[73,74,20],"text",{"x":75,"y":76,"style":77},"130","55","font-size:15px;font-weight:700;fill:var(--brand-strong,#4338ca);font-family:ui-monospace,SFMono-Regular,Menlo,monospace",[73,79,82],{"x":80,"y":76,"style":81},"260","font-size:15px;fill:var(--muted,#5b6780);font-family:ui-monospace,SFMono-Regular,Menlo,monospace","→",[73,84,87],{"x":85,"y":76,"style":86},"310","font-size:15px;font-weight:700;fill:var(--teal,#0f9488);font-family:ui-monospace,SFMono-Regular,Menlo,monospace","'Lookups'!",[73,89,92],{"x":90,"y":76,"style":91},"470","font-size:15px;font-weight:700;fill:var(--gold,#b4740a);font-family:ui-monospace,SFMono-Regular,Menlo,monospace","$A$2:$A$40",[94,95],"line",{"x1":75,"y1":96,"x2":97,"y2":98,"stroke":99,"style":100},"78","120","106","var(--muted,#5b6780)","stroke-width:1.5px",[94,102],{"x1":103,"y1":96,"x2":104,"y2":98,"stroke":99,"style":100},"350","330",[94,106],{"x1":107,"y1":96,"x2":108,"y2":98,"stroke":99,"style":100},"520","560",[56,110],{"x":65,"y":111,"width":112,"height":113,"rx":114,"fill":115,"stroke":116},"110","220","80","10","#f0f4ff","var(--line,#cdd5e6)",[73,118,121],{"x":119,"y":119,"style":120},"134","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","the name",[73,123,126],{"x":119,"y":124,"style":125},"156","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","letters, digits, underscore",[73,128,130],{"x":119,"y":129,"style":125},"176","no spaces, not \"A1\"",[56,132],{"x":133,"y":111,"width":112,"height":113,"rx":114,"fill":134,"stroke":135},"258","#d9f4f1","var(--teal,#0f9488)",[73,137,140],{"x":138,"y":119,"style":139},"368","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","the sheet",[73,142,143],{"x":138,"y":124,"style":125},"quote it when the title",[73,145,146],{"x":138,"y":129,"style":125},"contains a space",[56,148],{"x":149,"y":111,"width":112,"height":113,"rx":114,"fill":150,"stroke":151},"492","#fdefd8","var(--gold,#b4740a)",[73,153,156],{"x":154,"y":119,"style":155},"602","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","the range",[73,158,159],{"x":154,"y":124,"style":125},"absolute: $A$2:$A$40",[73,161,162],{"x":154,"y":129,"style":125},"or a single cell $B$1",[164,165,167],"h2",{"id":166},"prerequisites","Prerequisites",[169,170,175],"pre",{"className":171,"code":172,"language":173,"meta":174,"style":174},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,176,177],{"__ignoreMap":174},[178,179,181,185,189],"span",{"class":94,"line":180},1,[178,182,184],{"class":183},"sMTad","pip",[178,186,188],{"class":187},"srMev"," install",[178,190,191],{"class":187}," openpyxl\n",[10,193,194,195,197,198,201,202,205,206,209],{},"The ",[14,196,28],{}," class lives in ",[14,199,200],{},"openpyxl.workbook.defined_name",", and the collections it goes into are ",[14,203,204],{},"wb.defined_names"," and ",[14,207,208],{},"ws.defined_names",".",[164,211,213],{"id":212},"create-a-workbook-wide-name","Create a workbook-wide name",[10,215,216],{},"Add the name to the workbook and any sheet can use it:",[169,218,222],{"className":219,"code":220,"language":221,"meta":174,"style":174},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.workbook.defined_name import DefinedName\n\nwb = Workbook()\n\nlookups = wb.active\nlookups.title = \"Lookups\"\nlookups[\"A1\"] = \"Region\"\nfor i, region in enumerate([\"North\", \"South\", \"West\", \"Central\"], start=2):\n    lookups[f\"A{i}\"] = region\nlookups[\"C1\"] = \"VAT rate\"\nlookups[\"C2\"] = 0.20\n\nlast = lookups.max_row\nwb.defined_names.add(DefinedName(\"Regions\", attr_text=f\"'Lookups'!$A$2:$A${last}\"))\nwb.defined_names.add(DefinedName(\"TaxRate\", attr_text=\"'Lookups'!$C$2\"))\n\nreport = wb.create_sheet(\"Report\")\nreport[\"A1\"] = \"Net\"\nreport[\"B1\"] = 1000\nreport[\"A2\"] = \"VAT\"\nreport[\"B2\"] = \"=B1*TaxRate\"\nreport[\"A3\"] = \"Regions covered\"\nreport[\"B3\"] = \"=COUNTA(Regions)\"\n\nwb.save(\"named_ranges.xlsx\")\nprint(list(wb.defined_names))       # ['Regions', 'TaxRate']\n","python",[14,223,224,240,253,260,272,277,288,299,316,369,401,416,431,436,447,480,499,504,521,536,551,566,581,596,611,616,627],{"__ignoreMap":174},[178,225,226,230,234,237],{"class":94,"line":180},[178,227,229],{"class":228},"s-kum","from",[178,231,233],{"class":232},"skGVy"," openpyxl ",[178,235,236],{"class":228},"import",[178,238,239],{"class":232}," Workbook\n",[178,241,243,245,248,250],{"class":94,"line":242},2,[178,244,229],{"class":228},[178,246,247],{"class":232}," openpyxl.workbook.defined_name ",[178,249,236],{"class":228},[178,251,252],{"class":232}," DefinedName\n",[178,254,256],{"class":94,"line":255},3,[178,257,259],{"emptyLinePlaceholder":258},true,"\n",[178,261,263,266,269],{"class":94,"line":262},4,[178,264,265],{"class":232},"wb ",[178,267,268],{"class":228},"=",[178,270,271],{"class":232}," Workbook()\n",[178,273,275],{"class":94,"line":274},5,[178,276,259],{"emptyLinePlaceholder":258},[178,278,280,283,285],{"class":94,"line":279},6,[178,281,282],{"class":232},"lookups ",[178,284,268],{"class":228},[178,286,287],{"class":232}," wb.active\n",[178,289,291,294,296],{"class":94,"line":290},7,[178,292,293],{"class":232},"lookups.title ",[178,295,268],{"class":228},[178,297,298],{"class":187}," \"Lookups\"\n",[178,300,302,305,308,311,313],{"class":94,"line":301},8,[178,303,304],{"class":232},"lookups[",[178,306,307],{"class":187},"\"A1\"",[178,309,310],{"class":232},"] ",[178,312,268],{"class":228},[178,314,315],{"class":187}," \"Region\"\n",[178,317,319,322,325,328,332,335,338,341,344,346,349,351,354,357,361,363,366],{"class":94,"line":318},9,[178,320,321],{"class":228},"for",[178,323,324],{"class":232}," i, region ",[178,326,327],{"class":228},"in",[178,329,331],{"class":330},"sP0c6"," enumerate",[178,333,334],{"class":232},"([",[178,336,337],{"class":187},"\"North\"",[178,339,340],{"class":232},", ",[178,342,343],{"class":187},"\"South\"",[178,345,340],{"class":232},[178,347,348],{"class":187},"\"West\"",[178,350,340],{"class":232},[178,352,353],{"class":187},"\"Central\"",[178,355,356],{"class":232},"], ",[178,358,360],{"class":359},"sa561","start",[178,362,268],{"class":228},[178,364,365],{"class":330},"2",[178,367,368],{"class":232},"):\n",[178,370,372,375,378,381,385,388,391,394,396,398],{"class":94,"line":371},10,[178,373,374],{"class":232},"    lookups[",[178,376,377],{"class":228},"f",[178,379,380],{"class":187},"\"A",[178,382,384],{"class":383},"sSjpA","{",[178,386,387],{"class":232},"i",[178,389,390],{"class":383},"}",[178,392,393],{"class":187},"\"",[178,395,310],{"class":232},[178,397,268],{"class":228},[178,399,400],{"class":232}," region\n",[178,402,404,406,409,411,413],{"class":94,"line":403},11,[178,405,304],{"class":232},[178,407,408],{"class":187},"\"C1\"",[178,410,310],{"class":232},[178,412,268],{"class":228},[178,414,415],{"class":187}," \"VAT rate\"\n",[178,417,419,421,424,426,428],{"class":94,"line":418},12,[178,420,304],{"class":232},[178,422,423],{"class":187},"\"C2\"",[178,425,310],{"class":232},[178,427,268],{"class":228},[178,429,430],{"class":330}," 0.20\n",[178,432,434],{"class":94,"line":433},13,[178,435,259],{"emptyLinePlaceholder":258},[178,437,439,442,444],{"class":94,"line":438},14,[178,440,441],{"class":232},"last ",[178,443,268],{"class":228},[178,445,446],{"class":232}," lookups.max_row\n",[178,448,450,453,456,458,461,463,465,468,470,473,475,477],{"class":94,"line":449},15,[178,451,452],{"class":232},"wb.defined_names.add(DefinedName(",[178,454,455],{"class":187},"\"Regions\"",[178,457,340],{"class":232},[178,459,460],{"class":359},"attr_text",[178,462,268],{"class":228},[178,464,377],{"class":228},[178,466,467],{"class":187},"\"'Lookups'!$A$2:$A$",[178,469,384],{"class":383},[178,471,472],{"class":232},"last",[178,474,390],{"class":383},[178,476,393],{"class":187},[178,478,479],{"class":232},"))\n",[178,481,483,485,488,490,492,494,497],{"class":94,"line":482},16,[178,484,452],{"class":232},[178,486,487],{"class":187},"\"TaxRate\"",[178,489,340],{"class":232},[178,491,460],{"class":359},[178,493,268],{"class":228},[178,495,496],{"class":187},"\"'Lookups'!$C$2\"",[178,498,479],{"class":232},[178,500,502],{"class":94,"line":501},17,[178,503,259],{"emptyLinePlaceholder":258},[178,505,507,510,512,515,518],{"class":94,"line":506},18,[178,508,509],{"class":232},"report ",[178,511,268],{"class":228},[178,513,514],{"class":232}," wb.create_sheet(",[178,516,517],{"class":187},"\"Report\"",[178,519,520],{"class":232},")\n",[178,522,524,527,529,531,533],{"class":94,"line":523},19,[178,525,526],{"class":232},"report[",[178,528,307],{"class":187},[178,530,310],{"class":232},[178,532,268],{"class":228},[178,534,535],{"class":187}," \"Net\"\n",[178,537,539,541,544,546,548],{"class":94,"line":538},20,[178,540,526],{"class":232},[178,542,543],{"class":187},"\"B1\"",[178,545,310],{"class":232},[178,547,268],{"class":228},[178,549,550],{"class":330}," 1000\n",[178,552,554,556,559,561,563],{"class":94,"line":553},21,[178,555,526],{"class":232},[178,557,558],{"class":187},"\"A2\"",[178,560,310],{"class":232},[178,562,268],{"class":228},[178,564,565],{"class":187}," \"VAT\"\n",[178,567,569,571,574,576,578],{"class":94,"line":568},22,[178,570,526],{"class":232},[178,572,573],{"class":187},"\"B2\"",[178,575,310],{"class":232},[178,577,268],{"class":228},[178,579,580],{"class":187}," \"=B1*TaxRate\"\n",[178,582,584,586,589,591,593],{"class":94,"line":583},23,[178,585,526],{"class":232},[178,587,588],{"class":187},"\"A3\"",[178,590,310],{"class":232},[178,592,268],{"class":228},[178,594,595],{"class":187}," \"Regions covered\"\n",[178,597,599,601,604,606,608],{"class":94,"line":598},24,[178,600,526],{"class":232},[178,602,603],{"class":187},"\"B3\"",[178,605,310],{"class":232},[178,607,268],{"class":228},[178,609,610],{"class":187}," \"=COUNTA(Regions)\"\n",[178,612,614],{"class":94,"line":613},25,[178,615,259],{"emptyLinePlaceholder":258},[178,617,619,622,625],{"class":94,"line":618},26,[178,620,621],{"class":232},"wb.save(",[178,623,624],{"class":187},"\"named_ranges.xlsx\"",[178,626,520],{"class":232},[178,628,630,633,636,639,642],{"class":94,"line":629},27,[178,631,632],{"class":330},"print",[178,634,635],{"class":232},"(",[178,637,638],{"class":330},"list",[178,640,641],{"class":232},"(wb.defined_names))       ",[178,643,645],{"class":644},"s-wDw","# ['Regions', 'TaxRate']\n",[10,647,648,649,651],{},"The name is built from ",[14,650,472],{},", so it always covers the rows that exist. That is the practical reason to create names from Python rather than by hand: the range is generated from the data in the same run that wrote it.",[10,653,654],{},"Quoting the sheet name is only strictly required when the title contains a space or punctuation, but quoting it always is harmless and saves you from a title change breaking the reference later.",[164,656,658],{"id":657},"create-a-sheet-local-name","Create a sheet-local name",[10,660,661,662,665,666,669],{},"Adding the name to a worksheet limits its visibility to that sheet, so several tabs can each have their own ",[14,663,664],{},"Target"," or ",[14,667,668],{},"Rate",":",[169,671,673],{"className":219,"code":672,"language":221,"meta":174,"style":174},"from openpyxl import Workbook\nfrom openpyxl.workbook.defined_name import DefinedName\n\ntargets = {\"North\": 120000, \"South\": 90000, \"West\": 45000}\n\nwb = Workbook()\nfor region, target in targets.items():\n    ws = wb.create_sheet(region)\n    ws[\"A1\"] = \"Target\"\n    ws[\"B1\"] = target\n    ws[\"A2\"] = \"Actual\"\n    ws[\"B2\"] = 0\n    ws[\"A3\"] = \"Attainment\"\n    ws[\"B3\"] = \"=IF(Target=0,0,B2\u002FTarget)\"\n    ws[\"B3\"].number_format = \"0.0%\"\n    ws.defined_names.add(DefinedName(\"Target\", attr_text=f\"'{region}'!$B$1\"))\n\ndel wb[\"Sheet\"]\nwb.save(\"scoped_targets.xlsx\")\n\nfor name in wb.sheetnames:\n    print(name, list(wb[name].defined_names))\n",[14,674,675,685,695,699,738,742,750,762,772,786,799,812,825,838,851,865,896,900,914,923,927,939],{"__ignoreMap":174},[178,676,677,679,681,683],{"class":94,"line":180},[178,678,229],{"class":228},[178,680,233],{"class":232},[178,682,236],{"class":228},[178,684,239],{"class":232},[178,686,687,689,691,693],{"class":94,"line":242},[178,688,229],{"class":228},[178,690,247],{"class":232},[178,692,236],{"class":228},[178,694,252],{"class":232},[178,696,697],{"class":94,"line":255},[178,698,259],{"emptyLinePlaceholder":258},[178,700,701,704,706,709,711,714,717,719,721,723,726,728,730,732,735],{"class":94,"line":262},[178,702,703],{"class":232},"targets ",[178,705,268],{"class":228},[178,707,708],{"class":232}," {",[178,710,337],{"class":187},[178,712,713],{"class":232},": ",[178,715,716],{"class":330},"120000",[178,718,340],{"class":232},[178,720,343],{"class":187},[178,722,713],{"class":232},[178,724,725],{"class":330},"90000",[178,727,340],{"class":232},[178,729,348],{"class":187},[178,731,713],{"class":232},[178,733,734],{"class":330},"45000",[178,736,737],{"class":232},"}\n",[178,739,740],{"class":94,"line":274},[178,741,259],{"emptyLinePlaceholder":258},[178,743,744,746,748],{"class":94,"line":279},[178,745,265],{"class":232},[178,747,268],{"class":228},[178,749,271],{"class":232},[178,751,752,754,757,759],{"class":94,"line":290},[178,753,321],{"class":228},[178,755,756],{"class":232}," region, target ",[178,758,327],{"class":228},[178,760,761],{"class":232}," targets.items():\n",[178,763,764,767,769],{"class":94,"line":301},[178,765,766],{"class":232},"    ws ",[178,768,268],{"class":228},[178,770,771],{"class":232}," wb.create_sheet(region)\n",[178,773,774,777,779,781,783],{"class":94,"line":318},[178,775,776],{"class":232},"    ws[",[178,778,307],{"class":187},[178,780,310],{"class":232},[178,782,268],{"class":228},[178,784,785],{"class":187}," \"Target\"\n",[178,787,788,790,792,794,796],{"class":94,"line":371},[178,789,776],{"class":232},[178,791,543],{"class":187},[178,793,310],{"class":232},[178,795,268],{"class":228},[178,797,798],{"class":232}," target\n",[178,800,801,803,805,807,809],{"class":94,"line":403},[178,802,776],{"class":232},[178,804,558],{"class":187},[178,806,310],{"class":232},[178,808,268],{"class":228},[178,810,811],{"class":187}," \"Actual\"\n",[178,813,814,816,818,820,822],{"class":94,"line":418},[178,815,776],{"class":232},[178,817,573],{"class":187},[178,819,310],{"class":232},[178,821,268],{"class":228},[178,823,824],{"class":330}," 0\n",[178,826,827,829,831,833,835],{"class":94,"line":433},[178,828,776],{"class":232},[178,830,588],{"class":187},[178,832,310],{"class":232},[178,834,268],{"class":228},[178,836,837],{"class":187}," \"Attainment\"\n",[178,839,840,842,844,846,848],{"class":94,"line":438},[178,841,776],{"class":232},[178,843,603],{"class":187},[178,845,310],{"class":232},[178,847,268],{"class":228},[178,849,850],{"class":187}," \"=IF(Target=0,0,B2\u002FTarget)\"\n",[178,852,853,855,857,860,862],{"class":94,"line":449},[178,854,776],{"class":232},[178,856,603],{"class":187},[178,858,859],{"class":232},"].number_format ",[178,861,268],{"class":228},[178,863,864],{"class":187}," \"0.0%\"\n",[178,866,867,870,873,875,877,879,881,884,886,889,891,894],{"class":94,"line":482},[178,868,869],{"class":232},"    ws.defined_names.add(DefinedName(",[178,871,872],{"class":187},"\"Target\"",[178,874,340],{"class":232},[178,876,460],{"class":359},[178,878,268],{"class":228},[178,880,377],{"class":228},[178,882,883],{"class":187},"\"'",[178,885,384],{"class":383},[178,887,888],{"class":232},"region",[178,890,390],{"class":383},[178,892,893],{"class":187},"'!$B$1\"",[178,895,479],{"class":232},[178,897,898],{"class":94,"line":501},[178,899,259],{"emptyLinePlaceholder":258},[178,901,902,905,908,911],{"class":94,"line":506},[178,903,904],{"class":228},"del",[178,906,907],{"class":232}," wb[",[178,909,910],{"class":187},"\"Sheet\"",[178,912,913],{"class":232},"]\n",[178,915,916,918,921],{"class":94,"line":523},[178,917,621],{"class":232},[178,919,920],{"class":187},"\"scoped_targets.xlsx\"",[178,922,520],{"class":232},[178,924,925],{"class":94,"line":538},[178,926,259],{"emptyLinePlaceholder":258},[178,928,929,931,934,936],{"class":94,"line":553},[178,930,321],{"class":228},[178,932,933],{"class":232}," name ",[178,935,327],{"class":228},[178,937,938],{"class":232}," wb.sheetnames:\n",[178,940,941,944,947,949],{"class":94,"line":568},[178,942,943],{"class":330},"    print",[178,945,946],{"class":232},"(name, ",[178,948,638],{"class":330},[178,950,951],{"class":232},"(wb[name].defined_names))\n",[10,953,954,955,958,959,961,962,340,965,968],{},"Every sheet's ",[14,956,957],{},"B3"," uses the identical formula string, and each resolves ",[14,960,664],{}," to its own cell. Without sheet scope you would need ",[14,963,964],{},"North_Target",[14,966,967],{},"South_Target"," and so on, and the formula would have to be generated per sheet — more code and more to get wrong.",[37,970,46,975,46,978,46,981,46,984,46,989,46,995,46,999,46,1004,46,1009,46,1014,46,1019,46,1023,46,1028,46,1031,46,1035,46,1039,46,1042,46,1046,46,1049,46,1051,46,1055,46,1058,46,1061,46,1065],{"viewBox":971,"role":40,"ariaLabelledBy":972,"xmlns":44,"style":45},"0 0 740 226",[973,974],"nr-scope-t","nr-scope-d",[48,976,977],{"id":973},"Name resolution order between sheet-local and workbook names",[52,979,980],{"id":974},"A formula on the North sheet referring to Target first looks in the North sheet's own names and finds it. A formula referring to TaxRate finds nothing locally and falls through to the workbook-level names.",[56,982],{"x":58,"y":58,"width":59,"height":983,"fill":61},"226",[56,985],{"x":986,"y":987,"width":988,"height":64,"rx":68,"fill":69,"stroke":116},"30","26","230",[73,990,994],{"x":991,"y":992,"style":993},"145","52","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","formula on 'North'",[73,996,998],{"x":991,"y":997,"style":125},"74","=B2\u002FTarget and =B1*TaxRate",[94,1000],{"x1":1001,"y1":1002,"x2":1003,"y2":1002,"stroke":99,"style":100},"264","56","308",[1005,1006],"polygon",{"points":1007,"fill":1008},"312,56 300,50 300,62","#5b6780",[56,1010],{"x":1011,"y":65,"width":1012,"height":1013,"rx":68,"fill":134,"stroke":135,"style":71},"314","200","64",[73,1015,1018],{"x":1016,"y":1017,"style":139},"414","48","1 · sheet names",[73,1020,1022],{"x":1016,"y":1021,"style":125},"70","ws.defined_names: Target ✓",[94,1024],{"x1":1025,"y1":1002,"x2":1026,"y2":1002,"stroke":99,"style":1027},"518","562","stroke-width:1.5px;stroke-dasharray:4 3",[1005,1029],{"points":1030,"fill":1008},"566,56 554,50 554,62",[56,1032],{"x":1033,"y":65,"width":1034,"height":1013,"rx":68,"fill":115,"stroke":116},"568","150",[73,1036,1038],{"x":1037,"y":1017,"style":120},"643","match found",[73,1040,1041],{"x":1037,"y":1021,"style":125},"'North'!$B$1",[94,1043],{"x1":1016,"y1":1044,"x2":1016,"y2":1045,"stroke":99,"style":100},"92","122",[1005,1047],{"points":1048,"fill":1008},"414,126 408,114 420,114",[56,1050],{"x":1011,"y":75,"width":1012,"height":1013,"rx":68,"fill":150,"stroke":151,"style":71},[73,1052,1054],{"x":1016,"y":1053,"style":155},"154","2 · workbook names",[73,1056,1057],{"x":1016,"y":129,"style":125},"wb.defined_names: TaxRate ✓",[73,1059,1060],{"x":991,"y":1034,"style":125},"Local wins when both exist —",[73,1062,1064],{"x":991,"y":1063,"style":125},"172","handy, and easy to forget.",[73,1066,1068],{"x":1037,"y":1067,"style":125},"160","'Lookups'!$C$2",[10,1070,1071,1072,1075],{},"The resolution order in the diagram is worth remembering when a value looks wrong: a sheet-local ",[14,1073,1074],{},"TaxRate"," shadows the workbook one for formulas on that sheet only, so two tabs can show different results from an identical formula.",[164,1077,1079],{"id":1078},"point-a-name-at-a-whole-table-or-a-single-column","Point a name at a whole table or a single column",[10,1081,1082,1083,1086],{},"Names are not limited to one column. A rectangular range works for ",[14,1084,1085],{},"VLOOKUP"," tables, and a single cell works for an input:",[169,1088,1090],{"className":219,"code":1089,"language":221,"meta":174,"style":174},"from openpyxl import Workbook\nfrom openpyxl.workbook.defined_name import DefinedName\n\nwb = Workbook()\nprices = wb.active\nprices.title = \"Prices\"\nprices.append([\"SKU\", \"Unit_Price\"])\nfor sku, price in ((\"A-100\", 19.99), (\"B-200\", 49.50), (\"C-300\", 12.25)):\n    prices.append([sku, price])\n\nlast = prices.max_row\nwb.defined_names.add(DefinedName(\"PriceTable\", attr_text=f\"'Prices'!$A$2:$B${last}\"))\n\norders = wb.create_sheet(\"Orders\")\norders.append([\"SKU\", \"Quantity\", \"Unit_Price\"])\norders.append([\"A-100\", 4])\norders[\"C2\"] = '=IFERROR(VLOOKUP(A2,PriceTable,2,FALSE),\"unpriced\")'\n\nwb.save(\"price_table.xlsx\")\n",[14,1091,1092,1102,1112,1116,1124,1133,1143,1159,1203,1208,1212,1221,1249,1253,1267,1285,1298,1312,1316],{"__ignoreMap":174},[178,1093,1094,1096,1098,1100],{"class":94,"line":180},[178,1095,229],{"class":228},[178,1097,233],{"class":232},[178,1099,236],{"class":228},[178,1101,239],{"class":232},[178,1103,1104,1106,1108,1110],{"class":94,"line":242},[178,1105,229],{"class":228},[178,1107,247],{"class":232},[178,1109,236],{"class":228},[178,1111,252],{"class":232},[178,1113,1114],{"class":94,"line":255},[178,1115,259],{"emptyLinePlaceholder":258},[178,1117,1118,1120,1122],{"class":94,"line":262},[178,1119,265],{"class":232},[178,1121,268],{"class":228},[178,1123,271],{"class":232},[178,1125,1126,1129,1131],{"class":94,"line":274},[178,1127,1128],{"class":232},"prices ",[178,1130,268],{"class":228},[178,1132,287],{"class":232},[178,1134,1135,1138,1140],{"class":94,"line":279},[178,1136,1137],{"class":232},"prices.title ",[178,1139,268],{"class":228},[178,1141,1142],{"class":187}," \"Prices\"\n",[178,1144,1145,1148,1151,1153,1156],{"class":94,"line":290},[178,1146,1147],{"class":232},"prices.append([",[178,1149,1150],{"class":187},"\"SKU\"",[178,1152,340],{"class":232},[178,1154,1155],{"class":187},"\"Unit_Price\"",[178,1157,1158],{"class":232},"])\n",[178,1160,1161,1163,1166,1168,1171,1174,1176,1179,1182,1185,1187,1190,1192,1195,1197,1200],{"class":94,"line":301},[178,1162,321],{"class":228},[178,1164,1165],{"class":232}," sku, price ",[178,1167,327],{"class":228},[178,1169,1170],{"class":232}," ((",[178,1172,1173],{"class":187},"\"A-100\"",[178,1175,340],{"class":232},[178,1177,1178],{"class":330},"19.99",[178,1180,1181],{"class":232},"), (",[178,1183,1184],{"class":187},"\"B-200\"",[178,1186,340],{"class":232},[178,1188,1189],{"class":330},"49.50",[178,1191,1181],{"class":232},[178,1193,1194],{"class":187},"\"C-300\"",[178,1196,340],{"class":232},[178,1198,1199],{"class":330},"12.25",[178,1201,1202],{"class":232},")):\n",[178,1204,1205],{"class":94,"line":318},[178,1206,1207],{"class":232},"    prices.append([sku, price])\n",[178,1209,1210],{"class":94,"line":371},[178,1211,259],{"emptyLinePlaceholder":258},[178,1213,1214,1216,1218],{"class":94,"line":403},[178,1215,441],{"class":232},[178,1217,268],{"class":228},[178,1219,1220],{"class":232}," prices.max_row\n",[178,1222,1223,1225,1228,1230,1232,1234,1236,1239,1241,1243,1245,1247],{"class":94,"line":418},[178,1224,452],{"class":232},[178,1226,1227],{"class":187},"\"PriceTable\"",[178,1229,340],{"class":232},[178,1231,460],{"class":359},[178,1233,268],{"class":228},[178,1235,377],{"class":228},[178,1237,1238],{"class":187},"\"'Prices'!$A$2:$B$",[178,1240,384],{"class":383},[178,1242,472],{"class":232},[178,1244,390],{"class":383},[178,1246,393],{"class":187},[178,1248,479],{"class":232},[178,1250,1251],{"class":94,"line":433},[178,1252,259],{"emptyLinePlaceholder":258},[178,1254,1255,1258,1260,1262,1265],{"class":94,"line":438},[178,1256,1257],{"class":232},"orders ",[178,1259,268],{"class":228},[178,1261,514],{"class":232},[178,1263,1264],{"class":187},"\"Orders\"",[178,1266,520],{"class":232},[178,1268,1269,1272,1274,1276,1279,1281,1283],{"class":94,"line":449},[178,1270,1271],{"class":232},"orders.append([",[178,1273,1150],{"class":187},[178,1275,340],{"class":232},[178,1277,1278],{"class":187},"\"Quantity\"",[178,1280,340],{"class":232},[178,1282,1155],{"class":187},[178,1284,1158],{"class":232},[178,1286,1287,1289,1291,1293,1296],{"class":94,"line":482},[178,1288,1271],{"class":232},[178,1290,1173],{"class":187},[178,1292,340],{"class":232},[178,1294,1295],{"class":330},"4",[178,1297,1158],{"class":232},[178,1299,1300,1303,1305,1307,1309],{"class":94,"line":501},[178,1301,1302],{"class":232},"orders[",[178,1304,423],{"class":187},[178,1306,310],{"class":232},[178,1308,268],{"class":228},[178,1310,1311],{"class":187}," '=IFERROR(VLOOKUP(A2,PriceTable,2,FALSE),\"unpriced\")'\n",[178,1313,1314],{"class":94,"line":506},[178,1315,259],{"emptyLinePlaceholder":258},[178,1317,1318,1320,1323],{"class":94,"line":523},[178,1319,621],{"class":232},[178,1321,1322],{"class":187},"\"price_table.xlsx\"",[178,1324,520],{"class":232},[10,1326,1327,1330,1331,1335],{},[14,1328,1329],{},"VLOOKUP(A2, PriceTable, 2, FALSE)"," reads far better than the same call with a raw range, and when the price list grows you regenerate one name instead of hunting formulas. The same name is what you would hand to a ",[31,1332,1334],{"href":1333},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fadd-dropdown-data-validation-to-excel-with-openpyxl\u002F","dropdown validation list"," so the choices stay in step with the lookup table.",[164,1337,1339],{"id":1338},"list-replace-and-delete-names","List, replace and delete names",[10,1341,1342],{},"The collections behave like dictionaries, which makes maintenance straightforward:",[169,1344,1346],{"className":219,"code":1345,"language":221,"meta":174,"style":174},"from openpyxl import load_workbook\nfrom openpyxl.workbook.defined_name import DefinedName\n\nwb = load_workbook(\"named_ranges.xlsx\")\n\n# Inspect what is there\nfor name, defn in wb.defined_names.items():\n    print(f\"{name:12} -> {defn.attr_text}\")\n\n# Repoint a name at a longer range (delete, then add)\nif \"Regions\" in wb.defined_names:\n    del wb.defined_names[\"Regions\"]\nwb.defined_names.add(DefinedName(\"Regions\", attr_text=\"'Lookups'!$A$2:$A$500\"))\n\n# Remove one that is no longer used\nwb.defined_names.pop(\"TaxRate\", None)\n\nwb.save(\"named_ranges_updated.xlsx\")\n",[14,1347,1348,1359,1369,1373,1386,1390,1395,1407,1441,1445,1450,1464,1476,1493,1497,1502,1516,1520],{"__ignoreMap":174},[178,1349,1350,1352,1354,1356],{"class":94,"line":180},[178,1351,229],{"class":228},[178,1353,233],{"class":232},[178,1355,236],{"class":228},[178,1357,1358],{"class":232}," load_workbook\n",[178,1360,1361,1363,1365,1367],{"class":94,"line":242},[178,1362,229],{"class":228},[178,1364,247],{"class":232},[178,1366,236],{"class":228},[178,1368,252],{"class":232},[178,1370,1371],{"class":94,"line":255},[178,1372,259],{"emptyLinePlaceholder":258},[178,1374,1375,1377,1379,1382,1384],{"class":94,"line":262},[178,1376,265],{"class":232},[178,1378,268],{"class":228},[178,1380,1381],{"class":232}," load_workbook(",[178,1383,624],{"class":187},[178,1385,520],{"class":232},[178,1387,1388],{"class":94,"line":274},[178,1389,259],{"emptyLinePlaceholder":258},[178,1391,1392],{"class":94,"line":279},[178,1393,1394],{"class":644},"# Inspect what is there\n",[178,1396,1397,1399,1402,1404],{"class":94,"line":290},[178,1398,321],{"class":228},[178,1400,1401],{"class":232}," name, defn ",[178,1403,327],{"class":228},[178,1405,1406],{"class":232}," wb.defined_names.items():\n",[178,1408,1409,1411,1413,1415,1417,1419,1422,1425,1427,1430,1432,1435,1437,1439],{"class":94,"line":301},[178,1410,943],{"class":330},[178,1412,635],{"class":232},[178,1414,377],{"class":228},[178,1416,393],{"class":187},[178,1418,384],{"class":383},[178,1420,1421],{"class":232},"name",[178,1423,1424],{"class":228},":12",[178,1426,390],{"class":383},[178,1428,1429],{"class":187}," -> ",[178,1431,384],{"class":383},[178,1433,1434],{"class":232},"defn.attr_text",[178,1436,390],{"class":383},[178,1438,393],{"class":187},[178,1440,520],{"class":232},[178,1442,1443],{"class":94,"line":318},[178,1444,259],{"emptyLinePlaceholder":258},[178,1446,1447],{"class":94,"line":371},[178,1448,1449],{"class":644},"# Repoint a name at a longer range (delete, then add)\n",[178,1451,1452,1455,1458,1461],{"class":94,"line":403},[178,1453,1454],{"class":228},"if",[178,1456,1457],{"class":187}," \"Regions\"",[178,1459,1460],{"class":228}," in",[178,1462,1463],{"class":232}," wb.defined_names:\n",[178,1465,1466,1469,1472,1474],{"class":94,"line":418},[178,1467,1468],{"class":228},"    del",[178,1470,1471],{"class":232}," wb.defined_names[",[178,1473,455],{"class":187},[178,1475,913],{"class":232},[178,1477,1478,1480,1482,1484,1486,1488,1491],{"class":94,"line":433},[178,1479,452],{"class":232},[178,1481,455],{"class":187},[178,1483,340],{"class":232},[178,1485,460],{"class":359},[178,1487,268],{"class":228},[178,1489,1490],{"class":187},"\"'Lookups'!$A$2:$A$500\"",[178,1492,479],{"class":232},[178,1494,1495],{"class":94,"line":438},[178,1496,259],{"emptyLinePlaceholder":258},[178,1498,1499],{"class":94,"line":449},[178,1500,1501],{"class":644},"# Remove one that is no longer used\n",[178,1503,1504,1507,1509,1511,1514],{"class":94,"line":482},[178,1505,1506],{"class":232},"wb.defined_names.pop(",[178,1508,487],{"class":187},[178,1510,340],{"class":232},[178,1512,1513],{"class":330},"None",[178,1515,520],{"class":232},[178,1517,1518],{"class":94,"line":501},[178,1519,259],{"emptyLinePlaceholder":258},[178,1521,1522,1524,1527],{"class":94,"line":506},[178,1523,621],{"class":232},[178,1525,1526],{"class":187},"\"named_ranges_updated.xlsx\"",[178,1528,520],{"class":232},[10,1530,1531,1532,205,1535,1538],{},"Printing the inventory before you change anything is worth the two lines it costs. A workbook that has been round-tripped through several teams often carries names nobody recognises, including ones Excel created itself: ",[14,1533,1534],{},"Print_Area",[14,1536,1537],{},"Print_Titles"," are ordinary defined names under the surface, and a loop that blindly deletes everything will quietly change how the file prints.",[10,1540,1541,1542,1545],{},"There is no rename: the name is the key, so a rename is a delete plus an add — and any formula that used the old name will show ",[14,1543,1544],{},"#NAME?"," until you rewrite it. If you generate both the names and the formulas in the same script, keep the name strings in one constant at the top so the two never drift apart.",[164,1547,1549],{"id":1548},"naming-rules-that-excel-enforces","Naming rules that Excel enforces",[1551,1552,1553,1569],"table",{},[1554,1555,1556],"thead",{},[1557,1558,1559,1563,1566],"tr",{},[1560,1561,1562],"th",{},"Rule",[1560,1564,1565],{},"Valid",[1560,1567,1568],{},"Invalid",[1570,1571,1572,1590,1608,1632,1646,1657],"tbody",{},[1557,1573,1574,1578,1585],{},[1575,1576,1577],"td",{},"First character",[1575,1579,1580,340,1582],{},[14,1581,20],{},[14,1583,1584],{},"_2026Plan",[1575,1586,1587],{},[14,1588,1589],{},"2026Plan",[1557,1591,1592,1595,1600],{},[1575,1593,1594],{},"Allowed characters",[1575,1596,1597],{},[14,1598,1599],{},"Tax_Rate.2026",[1575,1601,1602,340,1605],{},[14,1603,1604],{},"Tax Rate",[14,1606,1607],{},"Tax-Rate",[1557,1609,1610,1613,1621],{},[1575,1611,1612],{},"Cannot look like a reference",[1575,1614,1615,340,1618],{},[14,1616,1617],{},"Q_3",[14,1619,1620],{},"Region1",[1575,1622,1623,340,1626,340,1629],{},[14,1624,1625],{},"Q3",[14,1627,1628],{},"A1",[14,1630,1631],{},"XFD1",[1557,1633,1634,1637,1640],{},[1575,1635,1636],{},"Reserved words",[1575,1638,1639],{},"anything else",[1575,1641,1642,340,1644],{},[14,1643,1534],{},[14,1645,1537],{},[1557,1647,1648,1651,1654],{},[1575,1649,1650],{},"Length",[1575,1652,1653],{},"up to 255 characters",[1575,1655,1656],{},"longer",[1557,1658,1659,1662,1670],{},[1575,1660,1661],{},"Case",[1575,1663,1664,1666,1667],{},[14,1665,20],{}," = ",[14,1668,1669],{},"REGIONS",[1575,1671,1672],{},"duplicates differing only by case",[37,1674,46,1680,46,1683,46,1686,46,1689,46,1693,46,1698,46,1704,46,1708,46,1711,46,1714,46,1717,46,1720,46,1725,46,1730,46,1733,46,1736,46,1741,46,1743,46,1745,46,1748,46,1750,46,1752],{"viewBox":1675,"role":40,"ariaLabelledBy":1676,"xmlns":44,"style":1679},"0 0 720 200",[1677,1678],"nr-valid-t","nr-valid-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[48,1681,1682],{"id":1677},"Names Excel accepts and names it rejects",[52,1684,1685],{"id":1678},"Three accepted names — Regions, Tax_Rate.2026 and _2026Plan — beside three rejected ones: Tax Rate contains a space, Q3 looks like a cell reference, and 2026Plan starts with a digit.",[56,1687],{"x":58,"y":58,"width":1688,"height":1012,"fill":61},"720",[56,1690],{"x":1691,"y":65,"width":104,"height":124,"rx":1692,"fill":134,"stroke":135,"style":71},"20","14",[73,1694,1697],{"x":1695,"y":67,"style":1696},"185","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","accepted",[56,1699],{"x":1700,"y":1013,"width":1701,"height":1702,"rx":1703,"fill":61,"stroke":116},"44","282","32","7",[73,1705,20],{"x":64,"y":1706,"style":1707},"86","font-size:12.5px;fill:var(--text,#172033);font-family:ui-monospace,Menlo,monospace",[56,1709],{"x":1700,"y":1710,"width":1701,"height":1702,"rx":1703,"fill":61,"stroke":116},"102",[73,1712,1599],{"x":64,"y":1713,"style":1707},"124",[56,1715],{"x":1700,"y":1716,"width":1701,"height":1702,"rx":1703,"fill":61,"stroke":116},"140",[73,1718,1584],{"x":64,"y":1719,"style":1707},"162",[56,1721],{"x":1722,"y":65,"width":104,"height":124,"rx":1692,"fill":1723,"stroke":1724,"style":71},"370","#fce9e9","var(--danger,#dc2626)",[73,1726,1729],{"x":1727,"y":67,"style":1728},"535","font-size:13px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","rejected by Excel",[56,1731],{"x":1732,"y":1013,"width":1701,"height":1702,"rx":1703,"fill":61,"stroke":116},"394",[73,1734,1604],{"x":1735,"y":1706,"style":1707},"410",[73,1737,1740],{"x":1738,"y":1706,"style":1739},"660","font-size:11px;fill:var(--danger,#dc2626);text-anchor:end","space",[56,1742],{"x":1732,"y":1710,"width":1701,"height":1702,"rx":1703,"fill":61,"stroke":116},[73,1744,1625],{"x":1735,"y":1713,"style":1707},[73,1746,1747],{"x":1738,"y":1713,"style":1739},"looks like a cell",[56,1749],{"x":1732,"y":1716,"width":1701,"height":1702,"rx":1703,"fill":61,"stroke":116},[73,1751,1589],{"x":1735,"y":1719,"style":1707},[73,1753,1754],{"x":1738,"y":1719,"style":1739},"starts with a digit",[10,1756,1757],{},"openpyxl does not validate these rules when you add the name — the file is written happily and Excel reports a problem on open. A one-line regular expression check before adding is cheap insurance in a generator that builds names from column headers.",[164,1759,1761],{"id":1760},"generate-names-from-your-data-model","Generate names from your data model",[10,1763,1764],{},"When the sheet layout comes from a Python structure, derive the names from the same structure so the two cannot disagree:",[169,1766,1768],{"className":219,"code":1767,"language":221,"meta":174,"style":174},"import re\nfrom openpyxl import Workbook\nfrom openpyxl.workbook.defined_name import DefinedName\n\nNAME_OK = re.compile(r\"^[A-Za-z_][A-Za-z0-9_.]{0,254}$\")\n\ninputs = {\"VAT rate\": 0.20, \"Discount threshold\": 500, \"FX EURGBP\": 0.86}\n\nwb = Workbook()\nws = wb.active\nws.title = \"Inputs\"\nws.append([\"Input\", \"Value\"])\n\nfor label, value in inputs.items():\n    ws.append([label, value])\n    row = ws.max_row\n    name = re.sub(r\"[^A-Za-z0-9_]\", \"_\", label)          # spaces are illegal in names\n    if not NAME_OK.match(name):\n        raise ValueError(f\"cannot build a valid name from {label!r}\")\n    wb.defined_names.add(DefinedName(name, attr_text=f\"'Inputs'!$B${row}\"))\n\nprint(list(wb.defined_names))     # ['VAT_rate', 'Discount_threshold', 'FX_EURGBP']\nwb.save(\"inputs_named.xlsx\")\n",[14,1769,1770,1777,1787,1797,1801,1842,1846,1885,1889,1897,1906,1916,1931,1935,1947,1952,1962,2000,2014,2043,2068,2072,2086],{"__ignoreMap":174},[178,1771,1772,1774],{"class":94,"line":180},[178,1773,236],{"class":228},[178,1775,1776],{"class":232}," re\n",[178,1778,1779,1781,1783,1785],{"class":94,"line":242},[178,1780,229],{"class":228},[178,1782,233],{"class":232},[178,1784,236],{"class":228},[178,1786,239],{"class":232},[178,1788,1789,1791,1793,1795],{"class":94,"line":255},[178,1790,229],{"class":228},[178,1792,247],{"class":232},[178,1794,236],{"class":228},[178,1796,252],{"class":232},[178,1798,1799],{"class":94,"line":262},[178,1800,259],{"emptyLinePlaceholder":258},[178,1802,1803,1806,1809,1812,1815,1817,1820,1823,1826,1829,1832,1835,1838,1840],{"class":94,"line":274},[178,1804,1805],{"class":330},"NAME_OK",[178,1807,1808],{"class":228}," =",[178,1810,1811],{"class":232}," re.compile(",[178,1813,1814],{"class":228},"r",[178,1816,393],{"class":187},[178,1818,1819],{"class":330},"^[",[178,1821,1822],{"class":383},"A-Za-z_",[178,1824,1825],{"class":330},"][",[178,1827,1828],{"class":383},"A-Za-z0-9_.",[178,1830,1831],{"class":330},"]",[178,1833,1834],{"class":228},"{0,254}",[178,1836,1837],{"class":330},"$",[178,1839,393],{"class":187},[178,1841,520],{"class":232},[178,1843,1844],{"class":94,"line":279},[178,1845,259],{"emptyLinePlaceholder":258},[178,1847,1848,1851,1853,1855,1858,1860,1863,1865,1868,1870,1873,1875,1878,1880,1883],{"class":94,"line":290},[178,1849,1850],{"class":232},"inputs ",[178,1852,268],{"class":228},[178,1854,708],{"class":232},[178,1856,1857],{"class":187},"\"VAT rate\"",[178,1859,713],{"class":232},[178,1861,1862],{"class":330},"0.20",[178,1864,340],{"class":232},[178,1866,1867],{"class":187},"\"Discount threshold\"",[178,1869,713],{"class":232},[178,1871,1872],{"class":330},"500",[178,1874,340],{"class":232},[178,1876,1877],{"class":187},"\"FX EURGBP\"",[178,1879,713],{"class":232},[178,1881,1882],{"class":330},"0.86",[178,1884,737],{"class":232},[178,1886,1887],{"class":94,"line":301},[178,1888,259],{"emptyLinePlaceholder":258},[178,1890,1891,1893,1895],{"class":94,"line":318},[178,1892,265],{"class":232},[178,1894,268],{"class":228},[178,1896,271],{"class":232},[178,1898,1899,1902,1904],{"class":94,"line":371},[178,1900,1901],{"class":232},"ws ",[178,1903,268],{"class":228},[178,1905,287],{"class":232},[178,1907,1908,1911,1913],{"class":94,"line":403},[178,1909,1910],{"class":232},"ws.title ",[178,1912,268],{"class":228},[178,1914,1915],{"class":187}," \"Inputs\"\n",[178,1917,1918,1921,1924,1926,1929],{"class":94,"line":418},[178,1919,1920],{"class":232},"ws.append([",[178,1922,1923],{"class":187},"\"Input\"",[178,1925,340],{"class":232},[178,1927,1928],{"class":187},"\"Value\"",[178,1930,1158],{"class":232},[178,1932,1933],{"class":94,"line":433},[178,1934,259],{"emptyLinePlaceholder":258},[178,1936,1937,1939,1942,1944],{"class":94,"line":438},[178,1938,321],{"class":228},[178,1940,1941],{"class":232}," label, value ",[178,1943,327],{"class":228},[178,1945,1946],{"class":232}," inputs.items():\n",[178,1948,1949],{"class":94,"line":449},[178,1950,1951],{"class":232},"    ws.append([label, value])\n",[178,1953,1954,1957,1959],{"class":94,"line":482},[178,1955,1956],{"class":232},"    row ",[178,1958,268],{"class":228},[178,1960,1961],{"class":232}," ws.max_row\n",[178,1963,1964,1967,1969,1972,1974,1976,1979,1982,1985,1987,1989,1991,1994,1997],{"class":94,"line":501},[178,1965,1966],{"class":232},"    name ",[178,1968,268],{"class":228},[178,1970,1971],{"class":232}," re.sub(",[178,1973,1814],{"class":228},[178,1975,393],{"class":187},[178,1977,1978],{"class":330},"[",[178,1980,1981],{"class":228},"^",[178,1983,1984],{"class":383},"A-Za-z0-9_",[178,1986,1831],{"class":330},[178,1988,393],{"class":187},[178,1990,340],{"class":232},[178,1992,1993],{"class":187},"\"_\"",[178,1995,1996],{"class":232},", label)          ",[178,1998,1999],{"class":644},"# spaces are illegal in names\n",[178,2001,2002,2005,2008,2011],{"class":94,"line":506},[178,2003,2004],{"class":228},"    if",[178,2006,2007],{"class":228}," not",[178,2009,2010],{"class":330}," NAME_OK",[178,2012,2013],{"class":232},".match(name):\n",[178,2015,2016,2019,2022,2024,2026,2029,2031,2034,2037,2039,2041],{"class":94,"line":523},[178,2017,2018],{"class":228},"        raise",[178,2020,2021],{"class":330}," ValueError",[178,2023,635],{"class":232},[178,2025,377],{"class":228},[178,2027,2028],{"class":187},"\"cannot build a valid name from ",[178,2030,384],{"class":383},[178,2032,2033],{"class":232},"label",[178,2035,2036],{"class":228},"!r",[178,2038,390],{"class":383},[178,2040,393],{"class":187},[178,2042,520],{"class":232},[178,2044,2045,2048,2050,2052,2054,2057,2059,2062,2064,2066],{"class":94,"line":538},[178,2046,2047],{"class":232},"    wb.defined_names.add(DefinedName(name, ",[178,2049,460],{"class":359},[178,2051,268],{"class":228},[178,2053,377],{"class":228},[178,2055,2056],{"class":187},"\"'Inputs'!$B$",[178,2058,384],{"class":383},[178,2060,2061],{"class":232},"row",[178,2063,390],{"class":383},[178,2065,393],{"class":187},[178,2067,479],{"class":232},[178,2069,2070],{"class":94,"line":553},[178,2071,259],{"emptyLinePlaceholder":258},[178,2073,2074,2076,2078,2080,2083],{"class":94,"line":568},[178,2075,632],{"class":330},[178,2077,635],{"class":232},[178,2079,638],{"class":330},[178,2081,2082],{"class":232},"(wb.defined_names))     ",[178,2084,2085],{"class":644},"# ['VAT_rate', 'Discount_threshold', 'FX_EURGBP']\n",[178,2087,2088,2090,2093],{"class":94,"line":583},[178,2089,621],{"class":232},[178,2091,2092],{"class":187},"\"inputs_named.xlsx\"",[178,2094,520],{"class":232},[10,2096,2097],{},"Every input cell now has a name that a formula elsewhere can use, and the validation stops an awkward label — one starting with a digit, say — from producing a workbook Excel offers to repair.",[164,2099,2101],{"id":2100},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[1551,2103,2104,2117],{},[1554,2105,2106],{},[1557,2107,2108,2111,2114],{},[1560,2109,2110],{},"Symptom",[1560,2112,2113],{},"Cause",[1560,2115,2116],{},"Fix",[1570,2118,2119,2136,2153,2164,2180,2195],{},[1557,2120,2121,2126,2129],{},[1575,2122,2123,2125],{},[14,2124,1544],{}," in Excel",[1575,2127,2128],{},"Name missing, misspelled, or removed",[1575,2130,2131,2132,2135],{},"Print ",[14,2133,2134],{},"list(wb.defined_names)"," and compare with the formula",[1557,2137,2138,2141,2144],{},[1575,2139,2140],{},"Name resolves to the wrong cells",[1575,2142,2143],{},"Reference was relative, not absolute",[1575,2145,2146,2147,2149,2150],{},"Use ",[14,2148,92],{},", not ",[14,2151,2152],{},"A2:A40",[1557,2154,2155,2158,2161],{},[1575,2156,2157],{},"Excel repairs the file on open",[1575,2159,2160],{},"Name is invalid, e.g. contains a space",[1575,2162,2163],{},"Validate names against the table above before adding",[1557,2165,2166,2169,2174],{},[1575,2167,2168],{},"Name works on one sheet only",[1575,2170,2171,2172],{},"It was added to ",[14,2173,208],{},[1575,2175,2176,2177,2179],{},"Add it to ",[14,2178,204],{}," for global scope",[1557,2181,2182,2185,2188],{},[1575,2183,2184],{},"Range stops covering new rows",[1575,2186,2187],{},"Name was written with a fixed end row",[1575,2189,2190,2191,2194],{},"Regenerate the name from ",[14,2192,2193],{},"ws.max_row"," on each run",[1557,2196,2197,2200,2203],{},[1575,2198,2199],{},"Deleting a sheet leaves a broken name",[1575,2201,2202],{},"The name still points at the removed title",[1575,2204,2205],{},"Delete related names when you delete a sheet",[164,2207,2209],{"id":2208},"performance-and-scale-notes","Performance and scale notes",[10,2211,2212,2213,2217],{},"Defined names cost nothing at runtime — they are a handful of entries in the workbook's XML, and Excel resolves them once per formula evaluation. The scale issue is a different one: a workbook that accumulates hundreds of stale names (a common outcome of years of copy-pasting sheets) opens slowly and shows a cluttered Name Manager. If your script regenerates a workbook from a ",[31,2214,2216],{"href":2215},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002F","template",", it is worth dropping names whose target sheet no longer exists as part of the run.",[164,2219,2221],{"id":2220},"conclusion","Conclusion",[10,2223,2224,2225,2227,2228,665,2230,2232],{},"Creating a named range is one ",[14,2226,28],{}," added to either ",[14,2229,204],{},[14,2231,208],{},", and the two decisions that matter are made before that call: use an absolute reference so the target cannot drift, and pick workbook scope for shared inputs versus sheet scope for values each tab owns. Generate the range end from the data you just wrote, keep name strings in one place, and clean up names whose sheets have gone.",[164,2234,2236],{"id":2235},"frequently-asked-questions","Frequently asked questions",[10,2238,2239,2243,2244,2247],{},[2240,2241,2242],"strong",{},"Does the reference need dollar signs?","\nYes, in practice. A defined name should point at an absolute reference such as ",[14,2245,2246],{},"'Rates'!$B$1",", otherwise the target shifts relative to whichever cell is active when the name is resolved.",[10,2249,2250,2253,2254,665,2256,209],{},[2240,2251,2252],{},"What characters are allowed in a name?","\nNames start with a letter or underscore and may contain letters, digits, underscores and periods. Spaces are not allowed, and a name must not look like a cell reference such as ",[14,2255,1628],{},[14,2257,1625],{},[10,2259,2260,2263,2264,2267,2268,2271],{},[2240,2261,2262],{},"How do I make a name that only one sheet can see?","\nAdd it to that worksheet's collection with ",[14,2265,2266],{},"ws.defined_names.add(...)"," instead of ",[14,2269,2270],{},"wb.defined_names.add(...)",". Several sheets can then reuse the same name for their own cells.",[10,2273,2274,2277,2278,2281],{},[2240,2275,2276],{},"Can I delete or rename a defined name?","\nDelete with ",[14,2279,2280],{},"del wb.defined_names[\"Name\"]"," and add a fresh one. There is no rename operation — the name is the dictionary key.",[164,2283,2285],{"id":2284},"related","Related",[10,2287,2288],{},"Up to the parent guide:",[2290,2291,2292],"ul",{},[2293,2294,2295,2297],"li",{},[31,2296,34],{"href":33}," — where named ranges fit in the formula workflow.",[10,2299,2300],{},"Related guides:",[2290,2302,2303,2310,2317,2323],{},[2293,2304,2305,2309],{},[31,2306,2308],{"href":2307},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl\u002F","Write a Formula to an Excel Cell with openpyxl"," — the formulas that consume these names.",[2293,2311,2312,2316],{},[31,2313,2315],{"href":2314},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python\u002F","Add SUM and AVERAGE Formulas with Python"," — aggregate over a named range.",[2293,2318,2319,2322],{},[31,2320,2321],{"href":1333},"Add Dropdown Data Validation to Excel with openpyxl"," — a list that reads from a name.",[2293,2324,2325,2329],{},[31,2326,2328],{"href":2327},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation"," — workbooks, sheets and cells in depth.",[2331,2332,2333],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":174,"searchDepth":242,"depth":242,"links":2335},[2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347],{"id":166,"depth":242,"text":167},{"id":212,"depth":242,"text":213},{"id":657,"depth":242,"text":658},{"id":1078,"depth":242,"text":1079},{"id":1338,"depth":242,"text":1339},{"id":1548,"depth":242,"text":1549},{"id":1760,"depth":242,"text":1761},{"id":2100,"depth":242,"text":2101},{"id":2208,"depth":242,"text":2209},{"id":2220,"depth":242,"text":2221},{"id":2235,"depth":242,"text":2236},{"id":2284,"depth":242,"text":2285},"2026-08-01","Add workbook-wide and sheet-local defined names with openpyxl, point them at ranges or single cells, use them in formulas and charts, and list or delete them safely.","md",[2352,2354,2356,2358],{"q":2242,"a":2353},"Yes, in practice. A defined name should point at an absolute reference such as 'Rates'!$B$1, otherwise the target shifts relative to whichever cell is active when the name is resolved.",{"q":2252,"a":2355},"Names start with a letter or underscore and may contain letters, digits, underscores and periods. Spaces are not allowed, and a name must not look like a cell reference such as A1 or Q3.",{"q":2262,"a":2357},"Add it to that worksheet's collection with ws.defined_names.add(...) instead of wb.defined_names.add(...). Several sheets can then reuse the same name for their own cells.",{"q":2276,"a":2359},"Delete with del wb.defined_names[\"Name\"] and add a fresh one. There is no rename operation — the name is the dictionary key.",{"breadcrumb":2361},[2362,2365,2368,2369],{"name":2363,"item":2364},"Home","\u002F",{"name":2366,"item":2367},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":34,"item":33},{"name":5,"item":2370},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl\u002F","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl",{"title":5,"description":2373},"Use DefinedName to create named ranges from Python: absolute references, workbook versus sheet scope, dynamic ranges built from your data, and how to inspect them.","create-named-range-in-excel-with-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl\u002Findex","how-to","F7ixRgUS7UQ2OUuz7UMGLduSUTm5psWfUVTVfbHMo-M",[2379,2383],{"title":2380,"path":2381,"stem":2382,"children":-1},"Copy a Formula Down a Column in openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcopy-formula-down-a-column-in-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcopy-formula-down-a-column-in-openpyxl\u002Findex",{"title":2384,"path":2385,"stem":2386,"children":-1},"Read Formula Results with openpyxl data_only","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002Findex",1785584462714]