[{"data":1,"prerenderedAt":766},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-scope-legb-rule-explained":3},{"id":4,"title":5,"body":6,"description":752,"difficulty":753,"extension":754,"framework":755,"frameworkSlug":103,"meta":756,"navigation":129,"order":126,"path":757,"qaPath":758,"seo":759,"stem":760,"subtopic":761,"topic":762,"topicSlug":763,"updated":764,"__hash__":765},"blog\u002Fblog\u002Fpython-scope-legb-rule-explained.md","Python Scope and the LEGB Rule Explained — global, nonlocal, and Closures",{"type":7,"value":8,"toc":742},"minimark",[9,14,48,52,59,98,200,213,217,224,279,300,304,314,386,400,404,413,502,523,527,534,594,605,657,661,668,701,711,715,738],[10,11,13],"h2",{"id":12},"how-python-finds-your-variables","How Python finds your variables",[15,16,17,18,22,23,27,28,30,31,35,36,39,40,43,44,47],"p",{},"When you write a name like ",[19,20,21],"code",{},"x",", Python has to decide ",[24,25,26],"em",{},"which"," ",[19,29,21],{}," you mean. The rule it uses\nis called ",[32,33,34],"strong",{},"LEGB",", and almost every confusing scope bug — ",[19,37,38],{},"UnboundLocalError",", closures\nthat \"capture the wrong value\", ",[19,41,42],{},"global","\u002F",[19,45,46],{},"nonlocal"," mix-ups — comes from misunderstanding\nit. This guide walks through name resolution from the ground up.",[10,49,51],{"id":50},"the-legb-rule","The LEGB rule",[15,53,54,55,58],{},"To resolve a name being ",[32,56,57],{},"read",", Python searches four scopes in order, stopping at the\nfirst match:",[60,61,62,69,75,81],"ul",{},[63,64,65,68],"li",{},[32,66,67],{},"L","ocal — names assigned inside the current function.",[63,70,71,74],{},[32,72,73],{},"E","nclosing — names in any enclosing function (for nested functions).",[63,76,77,80],{},[32,78,79],{},"G","lobal — names at the top level of the module.",[63,82,83,86,87,90,91,90,94,97],{},[32,84,85],{},"B","uilt-in — names like ",[19,88,89],{},"len",", ",[19,92,93],{},"print",[19,95,96],{},"range",".",[99,100,105],"pre",{"className":101,"code":102,"language":103,"meta":104,"style":104},"language-python shiki shiki-themes github-light github-dark","x = \"global\"\n\ndef outer():\n    x = \"enclosing\"\n    def inner():\n        print(x)     # finds \"enclosing\" — the nearest scope that has x\n    inner()\n\nouter()              # enclosing\n","python","",[19,106,107,124,131,144,155,166,180,186,191],{"__ignoreMap":104},[108,109,112,116,120],"span",{"class":110,"line":111},"line",1,[108,113,115],{"class":114},"sVt8B","x ",[108,117,119],{"class":118},"szBVR","=",[108,121,123],{"class":122},"sZZnC"," \"global\"\n",[108,125,127],{"class":110,"line":126},2,[108,128,130],{"emptyLinePlaceholder":129},true,"\n",[108,132,134,137,141],{"class":110,"line":133},3,[108,135,136],{"class":118},"def",[108,138,140],{"class":139},"sScJk"," outer",[108,142,143],{"class":114},"():\n",[108,145,147,150,152],{"class":110,"line":146},4,[108,148,149],{"class":114},"    x ",[108,151,119],{"class":118},[108,153,154],{"class":122}," \"enclosing\"\n",[108,156,158,161,164],{"class":110,"line":157},5,[108,159,160],{"class":118},"    def",[108,162,163],{"class":139}," inner",[108,165,143],{"class":114},[108,167,169,173,176],{"class":110,"line":168},6,[108,170,172],{"class":171},"sj4cs","        print",[108,174,175],{"class":114},"(x)     ",[108,177,179],{"class":178},"sJ8bj","# finds \"enclosing\" — the nearest scope that has x\n",[108,181,183],{"class":110,"line":182},7,[108,184,185],{"class":114},"    inner()\n",[108,187,189],{"class":110,"line":188},8,[108,190,130],{"emptyLinePlaceholder":129},[108,192,194,197],{"class":110,"line":193},9,[108,195,196],{"class":114},"outer()              ",[108,198,199],{"class":178},"# enclosing\n",[15,201,202,203,206,207,209,210,97],{},"If ",[19,204,205],{},"inner"," had its own ",[19,208,21],{},", that local would win. If no scope has the name, you get a\n",[19,211,212],{},"NameError",[10,214,216],{"id":215},"assignment-decides-the-scope","Assignment decides the scope",[15,218,219,220,223],{},"Here's the rule that catches everyone: ",[32,221,222],{},"assigning to a name anywhere in a function makes\nthat name local for the entire function"," — even before the assignment line runs.",[99,225,227],{"className":101,"code":226,"language":103,"meta":104,"style":104},"count = 0\n\ndef increment():\n    count = count + 1    # UnboundLocalError!\n    return count\n",[19,228,229,239,243,252,271],{"__ignoreMap":104},[108,230,231,234,236],{"class":110,"line":111},[108,232,233],{"class":114},"count ",[108,235,119],{"class":118},[108,237,238],{"class":171}," 0\n",[108,240,241],{"class":110,"line":126},[108,242,130],{"emptyLinePlaceholder":129},[108,244,245,247,250],{"class":110,"line":133},[108,246,136],{"class":118},[108,248,249],{"class":139}," increment",[108,251,143],{"class":114},[108,253,254,257,259,262,265,268],{"class":110,"line":146},[108,255,256],{"class":114},"    count ",[108,258,119],{"class":118},[108,260,261],{"class":114}," count ",[108,263,264],{"class":118},"+",[108,266,267],{"class":171}," 1",[108,269,270],{"class":178},"    # UnboundLocalError!\n",[108,272,273,276],{"class":110,"line":157},[108,274,275],{"class":118},"    return",[108,277,278],{"class":114}," count\n",[15,280,281,282,285,286,289,290,293,294,296,297,299],{},"Because ",[19,283,284],{},"count"," is assigned in ",[19,287,288],{},"increment",", Python treats it as a ",[24,291,292],{},"local"," throughout the\nfunction. The right-hand side then tries to read a local that hasn't been assigned yet — so\nit raises ",[19,295,38],{},", not a ",[19,298,212],{}," and not \"use the global one\".",[10,301,303],{"id":302},"the-global-keyword","The global keyword",[15,305,306,307,310,311,313],{},"To ",[32,308,309],{},"rebind"," a module-level name from inside a function, declare it ",[19,312,42],{},":",[99,315,317],{"className":101,"code":316,"language":103,"meta":104,"style":104},"count = 0\n\ndef increment():\n    global count\n    count = count + 1    # now refers to the module-level count\n    return count\n\nincrement()              # 1\nincrement()              # 2\n",[19,318,319,327,331,339,346,361,367,371,379],{"__ignoreMap":104},[108,320,321,323,325],{"class":110,"line":111},[108,322,233],{"class":114},[108,324,119],{"class":118},[108,326,238],{"class":171},[108,328,329],{"class":110,"line":126},[108,330,130],{"emptyLinePlaceholder":129},[108,332,333,335,337],{"class":110,"line":133},[108,334,136],{"class":118},[108,336,249],{"class":139},[108,338,143],{"class":114},[108,340,341,344],{"class":110,"line":146},[108,342,343],{"class":118},"    global",[108,345,278],{"class":114},[108,347,348,350,352,354,356,358],{"class":110,"line":157},[108,349,256],{"class":114},[108,351,119],{"class":118},[108,353,261],{"class":114},[108,355,264],{"class":118},[108,357,267],{"class":171},[108,359,360],{"class":178},"    # now refers to the module-level count\n",[108,362,363,365],{"class":110,"line":168},[108,364,275],{"class":118},[108,366,278],{"class":114},[108,368,369],{"class":110,"line":182},[108,370,130],{"emptyLinePlaceholder":129},[108,372,373,376],{"class":110,"line":188},[108,374,375],{"class":114},"increment()              ",[108,377,378],{"class":178},"# 1\n",[108,380,381,383],{"class":110,"line":193},[108,382,375],{"class":114},[108,384,385],{"class":178},"# 2\n",[15,387,388,389,391,392,395,396,399],{},"Note you only need ",[19,390,42],{}," to ",[32,393,394],{},"assign",". Reading a global needs no declaration —\nmutating a global list (",[19,397,398],{},"my_list.append(...)",") also needs none, because that's mutation,\nnot rebinding.",[10,401,403],{"id":402},"the-nonlocal-keyword","The nonlocal keyword",[15,405,406,408,409,412],{},[19,407,46],{}," is the enclosing-scope equivalent: it lets a nested function rebind a variable\nin the ",[24,410,411],{},"nearest enclosing function"," (not the module).",[99,414,416],{"className":101,"code":415,"language":103,"meta":104,"style":104},"def make_counter():\n    count = 0\n    def increment():\n        nonlocal count        # rebind the enclosing count\n        count += 1\n        return count\n    return increment\n\nc = make_counter()\nc(), c(), c()                 # (1, 2, 3)\n",[19,417,418,427,435,443,454,465,472,479,483,493],{"__ignoreMap":104},[108,419,420,422,425],{"class":110,"line":111},[108,421,136],{"class":118},[108,423,424],{"class":139}," make_counter",[108,426,143],{"class":114},[108,428,429,431,433],{"class":110,"line":126},[108,430,256],{"class":114},[108,432,119],{"class":118},[108,434,238],{"class":171},[108,436,437,439,441],{"class":110,"line":133},[108,438,160],{"class":118},[108,440,249],{"class":139},[108,442,143],{"class":114},[108,444,445,448,451],{"class":110,"line":146},[108,446,447],{"class":118},"        nonlocal",[108,449,450],{"class":114}," count        ",[108,452,453],{"class":178},"# rebind the enclosing count\n",[108,455,456,459,462],{"class":110,"line":157},[108,457,458],{"class":114},"        count ",[108,460,461],{"class":118},"+=",[108,463,464],{"class":171}," 1\n",[108,466,467,470],{"class":110,"line":168},[108,468,469],{"class":118},"        return",[108,471,278],{"class":114},[108,473,474,476],{"class":110,"line":182},[108,475,275],{"class":118},[108,477,478],{"class":114}," increment\n",[108,480,481],{"class":110,"line":188},[108,482,130],{"emptyLinePlaceholder":129},[108,484,485,488,490],{"class":110,"line":193},[108,486,487],{"class":114},"c ",[108,489,119],{"class":118},[108,491,492],{"class":114}," make_counter()\n",[108,494,496,499],{"class":110,"line":495},10,[108,497,498],{"class":114},"c(), c(), c()                 ",[108,500,501],{"class":178},"# (1, 2, 3)\n",[15,503,504,505,90,507,510,511,513,514,516,517,519,520,522],{},"Without ",[19,506,46],{},[19,508,509],{},"count += 1"," would make ",[19,512,284],{}," local to ",[19,515,288],{}," and raise\n",[19,518,38],{},". ",[19,521,46],{}," requires an existing binding in an enclosing function — it\ncan't reach the global scope and it can't create a new variable.",[10,524,526],{"id":525},"the-late-binding-closure-trap","The late-binding closure trap",[15,528,529,530,533],{},"Closures capture ",[32,531,532],{},"variables, not values"," — they look the variable up when called, not\nwhen defined. This bites in loops:",[99,535,537],{"className":101,"code":536,"language":103,"meta":104,"style":104},"funcs = [lambda: i for i in range(3)]\n[f() for f in funcs]          # [2, 2, 2] — not [0, 1, 2]!\n",[19,538,539,576],{"__ignoreMap":104},[108,540,541,544,546,549,552,555,558,561,564,567,570,573],{"class":110,"line":111},[108,542,543],{"class":114},"funcs ",[108,545,119],{"class":118},[108,547,548],{"class":114}," [",[108,550,551],{"class":118},"lambda",[108,553,554],{"class":114},": i ",[108,556,557],{"class":118},"for",[108,559,560],{"class":114}," i ",[108,562,563],{"class":118},"in",[108,565,566],{"class":171}," range",[108,568,569],{"class":114},"(",[108,571,572],{"class":171},"3",[108,574,575],{"class":114},")]\n",[108,577,578,581,583,586,588,591],{"class":110,"line":126},[108,579,580],{"class":114},"[f() ",[108,582,557],{"class":118},[108,584,585],{"class":114}," f ",[108,587,563],{"class":118},[108,589,590],{"class":114}," funcs]          ",[108,592,593],{"class":178},"# [2, 2, 2] — not [0, 1, 2]!\n",[15,595,596,597,600,601,604],{},"All three lambdas share the same ",[19,598,599],{},"i",", which is ",[19,602,603],{},"2"," by the time they run. The fix is to bind\nthe current value with a default argument (evaluated at definition time):",[99,606,608],{"className":101,"code":607,"language":103,"meta":104,"style":104},"funcs = [lambda i=i: i for i in range(3)]\n[f() for f in funcs]          # [0, 1, 2]\n",[19,609,610,642],{"__ignoreMap":104},[108,611,612,614,616,618,620,623,625,628,630,632,634,636,638,640],{"class":110,"line":111},[108,613,543],{"class":114},[108,615,119],{"class":118},[108,617,548],{"class":114},[108,619,551],{"class":118},[108,621,622],{"class":114}," i",[108,624,119],{"class":118},[108,626,627],{"class":114},"i: i ",[108,629,557],{"class":118},[108,631,560],{"class":114},[108,633,563],{"class":118},[108,635,566],{"class":171},[108,637,569],{"class":114},[108,639,572],{"class":171},[108,641,575],{"class":114},[108,643,644,646,648,650,652,654],{"class":110,"line":126},[108,645,580],{"class":114},[108,647,557],{"class":118},[108,649,585],{"class":114},[108,651,563],{"class":118},[108,653,590],{"class":114},[108,655,656],{"class":178},"# [0, 1, 2]\n",[10,658,660],{"id":659},"comprehensions-have-their-own-scope","Comprehensions have their own scope",[15,662,663,664,667],{},"Since Python 3, comprehensions run in their ",[32,665,666],{},"own scope",", so the loop variable doesn't\nleak into the surrounding function:",[99,669,671],{"className":101,"code":670,"language":103,"meta":104,"style":104},"[y for y in range(3)]\ny                              # NameError — y did not leak out\n",[19,672,673,693],{"__ignoreMap":104},[108,674,675,678,680,683,685,687,689,691],{"class":110,"line":111},[108,676,677],{"class":114},"[y ",[108,679,557],{"class":118},[108,681,682],{"class":114}," y ",[108,684,563],{"class":118},[108,686,566],{"class":171},[108,688,569],{"class":114},[108,690,572],{"class":171},[108,692,575],{"class":114},[108,694,695,698],{"class":110,"line":126},[108,696,697],{"class":114},"y                              ",[108,699,700],{"class":178},"# NameError — y did not leak out\n",[15,702,703,704,706,707,710],{},"This is different from a plain ",[19,705,557],{}," loop, where the loop variable ",[24,708,709],{},"does"," survive after the\nloop ends.",[10,712,714],{"id":713},"recap","Recap",[15,716,717,718,720,721,724,725,727,728,732,733,737],{},"Python resolves names with ",[32,719,34],{},": Local, Enclosing, Global, Built-in, stopping at the\nfirst match. ",[32,722,723],{},"Assignment anywhere in a function makes the name local for the whole\nfunction",", which is the source of ",[19,726,38],{},". Use ",[32,729,730],{},[19,731,42],{}," to rebind a\nmodule-level name and ",[32,734,735],{},[19,736,46],{}," to rebind an enclosing function's name — both are only\nneeded for assignment, not for reading or mutating. Remember that closures capture\nvariables (late binding), so capture per-iteration values with a default argument when you\nbuild functions in a loop.",[739,740,741],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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);}",{"title":104,"searchDepth":126,"depth":126,"links":743},[744,745,746,747,748,749,750,751],{"id":12,"depth":126,"text":13},{"id":50,"depth":126,"text":51},{"id":215,"depth":126,"text":216},{"id":302,"depth":126,"text":303},{"id":402,"depth":126,"text":403},{"id":525,"depth":126,"text":526},{"id":659,"depth":126,"text":660},{"id":713,"depth":126,"text":714},"How Python resolves names with the LEGB rule, why assignment makes a name local (and triggers UnboundLocalError), and what the global and nonlocal keywords actually do.","medium","md","Python",{},"\u002Fblog\u002Fpython-scope-legb-rule-explained","\u002Fpython\u002Ffundamentals\u002Fscope-legb",{"title":5,"description":752},"blog\u002Fpython-scope-legb-rule-explained","Variables, Scope & the LEGB Rule","Fundamentals","fundamentals","2026-06-19","PfAlWp9GVR-yueFAqyEZyB9FZZq1FaAccqNsbXK2prY",1782244093084]