1.2.30 · D5 · HinglishIntroduction to Programming (Python)

Question bankVariable scope — LEGB rule (Local, Enclosing, Global, Built-in)

1,939 words9 min read↑ Read in English

1.2.30 · D5 · Coding › Introduction to Programming (Python) › Variable scope — LEGB rule (Local, Enclosing, Global, Built-


True or false — justify

Har jawab mein kyun bhi bolo, sirf T/F nahi.

Reading a global variable from inside a function requires the global keyword
False. Reading bahar ki taraf search karta hai (L→E→G→B), toh ek global free mein visible hota hai. Aapko global sirf tab chahiye jab aap use rebind (write) karna chahte ho.
global x creates the global variable x if it doesn't exist yet
True (kind of). Agar x abhi module namespace mein nahi hai, toh global x ke baad use assign karna wahan create kar deta hai. nonlocal ke unlike, global ke liye name ka pehle se exist karna zaroori nahi.
nonlocal x works even when there is no enclosing function that assigns x
False. nonlocal nearest enclosing function ke x se bind karta hai, toh woh x already ek enclosing scope mein exist karna chahiye, warna compile time pe SyntaxError aata hai.
A for loop introduces a new scope for its loop variable
False. Python mein sirf functions, modules, aur comprehensions scope create karte hain. Loop variable surrounding function/module mein leak ho jaata hai aur loop khatam hone ke baad bhi survive karta hai.
The name len cannot be reassigned because it is built-in
False. Aap module level pe len = 5 likh sakte ho; yeh built-in ko G mein shadow karta hai, aur baad mein len(...) ke calls fail ho jaate hain kyunki G ko B se pehle search kiya jaata hai.
If a function only reads count (never assigns it), Python looks up count via LEGB
True. Function body mein count ka koi assignment na hone se, count local nahi hai, toh Python normally L→E→G→B search karta hai.
Adding a single line count = 5 at the bottom of a function changes how count is read at the top
True. Python compile time pe poori function scan karta hai; ek assignment kahin bhi hone se name ko poori body ke liye Local mark kar deta hai, toh upar ki ek earlier read UnboundLocalError ban jaati hai.
A comprehension like [i for i in range(3)] leaks i into the surrounding scope
False (Python 3). Comprehensions ka apna scope hota hai, toh i survive nahi karta. Yeh ek plain for loop se alag hai, jo leak karta hai.
Two functions can each have their own local x without interfering
True. Har function call ko apna local namespace milta hai, toh ek mein x doosre ke x name-binding se bilkul alag hai — yahi scope ka poora point hai.
global and nonlocal are interchangeable ways to edit an outer variable
False. global module scope (G) ko target karta hai; nonlocal nearest enclosing function scope (E) ko. Yeh dono alag alag rooms ko point karte hain.

Spot the error

Snippet padhein, exact failure aur uski wajah batayein.

x = 10
def f():
    print(x)
    x = 20
f()
``` ::: **`UnboundLocalError`.** Assignment `x = 20` se `x` poori `f` ke liye Local ho jaata hai, toh earlier `print(x)` ek aisi local ko read karta hai jiska abhi koi value nahi — global `x = 10` kabhi consult nahi hota.
 
```python
def outer():
    def inner():
        nonlocal n
        n = 5
    inner()
outer()
``` ::: **`SyntaxError` (no binding for nonlocal `n`).** `nonlocal n` ko ek `n` chahiye jo already ek enclosing function mein exist kare, lekin `outer` ne kabhi koi define nahi kiya. Enclosing scope ko pehle name supply karna padta hai.
 
```python
def bump():
    total += 1
total = 0
bump()
``` ::: **`UnboundLocalError`.** `total += 1` ek read-then-write hai, toh `total` Local hai; assignment se pehle use read karna fail karta hai. Fix ke liye `bump` ke andar `global total` likhein.
 
```python
print = "hello"
print("done")
``` ::: **`TypeError: 'str' object is not callable`.** `print` ko assign karna built-in ko **G** mein shadow kar deta hai; call ab ek string ko call karne ki koshish karta hai. Koi error tab tak nahi aata jab tak aap use *call* nahi karte.
 
```python
def f():
    global y
    return y
f()
``` ::: **`NameError: name 'y' is not defined`.** `global y` kehta hai "module-level `y` use karo", lekin module level pe koi `y` exist nahi karta aur yahan koi assign bhi nahi hota, toh read kuch nahi paata.
 
```python
def counter():
    def step():
        count = count + 1
        return count
    count = 0
    return step()
counter()
``` ::: **`UnboundLocalError` inside `step`.** `count = count + 1` se `count` `step` ka local ban jaata hai; `step` kabhi `outer` ka `count` nahi dekhta. Enclosing scope tak pahunchne ke liye `nonlocal count` chahiye.
 
---
 
## Why questions
 
Yahan *reasoning* important hai, toh answers ek-do sentences ke hain.
 
Why does Python decide a name is local at *compile* time, not while running? ::: Kyunki yeh har function body ko pehle se ek baar scan karta hai kisi bhi assignment target ke liye; isse use fast local-variable slots allocate karne ki suvidha milti hai, dictionary lookups ki jagah — lekin iske saath "assignment kahin bhi hone se woh har jagah local ho jaata hai" rule ka cost bhi hai.
Why is `UnboundLocalError` a *different* error from `NameError`? ::: `NameError` ka matlab hai name kisi bhi scope mein nahi mila; `UnboundLocalError` ka matlab hai Python *jaanta hai* name Local hai (assignment ne use mark kiya) lekin aapne use value milne se pehle read kiya. Name ek slot ke roop mein exist karta hai lekin khali hai.
Why can you `list.append(item)` inside a function without `global`, but not `list = list + [item]`? ::: `append` existing object ko *mutate* karta hai jis par name already point karta hai — koi name rebind nahi hota, toh koi scope rule fire nahi hota. `list = ...` name ko *rebind* karta hai, jo local-creation rule trigger karta hai.
Why does `len("hi")` work with no import while `sqrt(4)` does not? ::: `len` **Built-in (B)** scope mein rehta hai jise LEGB hamesha last mein search karta hai, toh yeh hamesha mil jaata hai. `sqrt` built-in nahi hai; yeh `math` module mein rehta hai aur **G** mein pehle import karna padta hai.
Why does shadowing a built-in only cause trouble *later*, not at the assignment line? ::: Assignment `len = 5` simply **G** mein ek name bind karta hai — bilkul legal hai. Clash tabhi surface hota hai jab baad mein koi code `len` ko original built-in behaviour ki ummeed mein *use* karne ki koshish karta hai.
Why do closures need the **Enclosing (E)** scope specifically, not Global? ::: Outer function ke har call se ek *fresh* enclosing namespace banta hai, toh har closure apni private state capture kar sakta hai (jaise ek per-call counter) jo na koi doosra call dekh sake aur na module.
Why doesn't an `if` block get its own scope like it does in C or Java? ::: Python scope *function/module/comprehension boundaries* se define karta hai, `{}` blocks se nahi. `if` ke andar assign hua name simply surrounding function ya module ka ho jaata hai, toh `if` ke baad bhi visible rehta hai.
 
---
 
## Edge cases
 
Boundary aur degenerate situations jinhein rules ko phir bhi cover karna padta hai.
 
A name is assigned *and* read only inside one branch of an `if` that never runs — is it Local? ::: **Haan, Local.** Locality *source mein assignment statement ke hone* se decide hoti hai, is baat se nahi ki woh statement execute hoti hai ya nahi. Kahin aur use padhne se phir bhi `UnboundLocalError` aa sakta hai.
A function with `global x` but `x` also passed as a parameter named `x` — legal? ::: **Nahi, `SyntaxError`.** Ek name ek hi function mein ek parameter (jo Local hai) bhi nahi ho sakta aur `global` bhi declare nahi ho sakta; yeh dono bindings contradict karti hain.
Nested `nonlocal` — inner function reaches which enclosing `x`? ::: **Nearest** enclosing function jo `x` rakhta hai. Python enclosing functions ke through bahar ki taraf walk karta hai (E), bina `x` waale ko skip karta hai, aur pehle wale pe ruk jaata hai jiske paas `x` hai; yeh kabhi Global tak nahi pahuNchta.
Assigning to a global via `globals()['x'] = 5` from inside a function without `global` — does it work? ::: **Haan.** `globals()` directly module namespace dict return karta hai, aur us dict mein likhna local-creation rule ko entirely bypass kar deta hai — `global` keyword ki zaroorat nahi. (Yeh readability ko bhi bypass karta hai; `global` prefer karein.)
Reading a name that exists in Enclosing *and* Global — which wins? ::: **Enclosing (E)**, kyunki LEGB, G se pehle E search karta hai. Pehla match search rok deta hai, toh outer function ka variable module-level wale ko shadow kar deta hai.
A module-level (top-level) `x = x + 1` with no prior `x` — what happens? ::: **`NameError`.** Module level pe koi alag "local" nahi hota — module scope *hi* global scope hai — toh read simply kahin bhi `x` nahi paati aur `NameError` raise karta hai, `UnboundLocalError` nahi.
Can two comprehensions in the same function both use `x` without clashing? ::: **Haan.** Python 3 mein har comprehension ka apna scope hota hai, toh unke internal `x` bindings independent hain aur na function mein leak hote hain na ek doosre mein.
 
---
 
> [!recall]- Ek-line summary lock karne ke liye
> Yahan lagbhag har trap really wohi ek sawal hai: *kya aap ek name read kar rahe ho, ek name rebind kar rahe ho, ya ek object mutate kar rahe ho?* Woh verb sahi kar lo aur LEGB kabhi surprise nahi karega.
 
---
 
## Connections
- [[1.2.30 Variable scope — LEGB rule (Local, Enclosing, Global, Built-in) (Hinglish)|Parent topic — LEGB]] — woh rules jinhe yeh traps stress-test karte hain.
- [[NameError and UnboundLocalError]] — woh do errors jo zyaadatar traps produce karte hain.
- [[Closures and Nested Functions]] — Enclosing-scope ke edge cases.
- [[Mutable vs Immutable arguments]] — mutate-vs-rebind distinction.
- [[Namespaces and the dict model]] — kyun `globals()[...]`, `global` ko bypass karta hai.
- [[Modules and import]] — kyun `len` kaam karta hai lekin `sqrt` ko importing chahiye.