BASIC Python (Beginner Level) 📂 Basic Python Programming · 13 of 15 179 min read

Python Function Scopes — LEGB, Global vs Local

Master how variables behave inside Python functions: the LEGB scope lookup order, local vs global vs enclosing scope, why reading a global works but assigning creates a local shadow, the famous UnboundLocalError: referenced before assignment trap, the global and nonlocal keywords, calling default arguments by keyword to skip earlier ones, the mutable-default trap, and pass-by-object-reference behaviour with mutable vs immutable objects.

🌐
SectionSection第 01 节

Why Scope Exists — The Rooms in Your House 🏠Scope क्यों होता है — आपके घर के कमरे 🏠为什么需要作用域 — 你家里的房间 🏠

The Whiteboard in Every Roomहर कमरे में एक Whiteboard每个房间里的白板
Imagine a large house where every room has its own whiteboard. You write a phone number on the kitchen whiteboard. Then you walk into the bedroom and write a different number on that whiteboard with the same label — "contact". Later, in the kitchen, someone asks for "contact", and you read the kitchen whiteboard, not the bedroom one.

Python functions work like these rooms. Each function call has its own private whiteboard — a local scope. Names written there don't leak into other rooms. This is what keeps a 50,000-line codebase sane: two functions can both use a variable called i without getting in each other's way.
एक बड़े घर की कल्पना करें जिसके हर कमरे में अपना whiteboard है। आप रसोई के whiteboard पर एक फ़ोन नंबर लिखते हैं। फिर शयनकक्ष में जाकर उसके whiteboard पर उसी नाम — "contact" — से दूसरा नंबर लिखते हैं। बाद में रसोई में कोई "contact" पूछे, तो आप रसोई वाला whiteboard पढ़ते हैं, शयनकक्ष वाला नहीं।

Python functions इन्हीं कमरों जैसे काम करते हैं। हर function call का अपना निजी whiteboard होता है — एक local scope। वहाँ लिखे नाम दूसरे कमरों में नहीं रिसते। यही 50,000 lines के codebase को सँभाले रखता है: दो functions दोनों i नाम का variable रख सकते हैं, एक-दूसरे के रास्ते में आए बिना।
想象一栋大房子,每个房间都有自己的白板。你在厨房的白板上写了一个电话号码。然后走进卧室,在那里的白板上用 同样的标签 — "contact" — 写了另一个号码。后来在厨房里有人问 "contact",你读的是厨房的白板,而不是卧室的。

Python 函数就像这些房间。每次函数调用都有自己私有的白板 — 局部作用域。写在那里的名字不会泄漏到其他房间。这正是让 5 万行代码库保持清醒的原因:两个函数都可以用名为 i 的变量,互不干扰。

A scope is the part of a program where a name (a variable, function or class) can be used directly. Python decides which value a name means by looking through scopes in a fixed order. Knowing that order is the difference between "the code works" and "I have no idea why the code works".

Scope program का वह हिस्सा है जहाँ कोई नाम (variable, function या class) सीधे इस्तेमाल हो सकता है। Python तय क्रम में scopes देखकर तय करता है कि किसी नाम का मतलब कौन-सी value है। वह क्रम जानना ही "code चलता है" और "मुझे नहीं पता code क्यों चलता है" के बीच का अंतर है।

作用域 是程序中可以直接使用某个名字(变量、函数或类)的区域。Python 按固定顺序查看各个作用域,来决定一个名字对应哪个值。懂得这个顺序,就是 "代码能跑" 和 "我不知道代码为什么能跑" 之间的区别。

Three Variables Called i, No Clashi नाम के तीन Variables, कोई टकराव नहीं三个都叫 i 的变量,互不冲突
each call gets its own roomglobal room (module)global कमरा (module)全局房间(模块)i = 100count_apples()i = 3its own local roomइसका अपना local कमरा它自己的局部房间count_pears()i = 7its own local roomइसका अपना local कमरा它自己的局部房间

Each function call writes on its own whiteboard. The global i stays 100 whatever the functions do with their own i.

हर function call अपने whiteboard पर लिखता है। Functions अपने i के साथ कुछ भी करें, global i 100 ही रहता है।

每次函数调用都写在自己的白板上。不管函数怎样处理自己的 i,全局的 i 始终是 100。

🏠
The Core Insightमूल बात核心要点

Every time a function runs, Python creates a fresh local namespace for that call and throws it away when the function returns. Names created inside a function are born and die with that call — unless you say otherwise.

हर बार function चलने पर Python उस call के लिए एक नया local namespace बनाता है और function लौटते ही उसे फेंक देता है। Function के अंदर बने नाम उसी call के साथ पैदा होते और मरते हैं — जब तक आप कुछ और न कहें।

每次函数运行,Python 都会为这次调用创建一个全新的 局部命名空间,并在函数返回时丢弃它。函数内部创建的名字随这次调用诞生和消亡 — 除非你另有说明。

▶️
This Tutorial Has Six Simulators and a Quizइस Tutorial में छह Simulators और एक Quiz है本教程有六个模拟器和一个小测验

Look for the interactive boxes: the LEGB Name Finder (Section 02), the Scope Playground in Sections 03, 05, 07 and 08, and the Assignment Detector (07). Every function call gets its own room with a whiteboard; you can watch names being written, searched for and destroyed. Click ✎ Edit code to run your own programs — the results and errors match real Python.

Interactive डिब्बे खोजें: LEGB Name Finder (Section 02), Sections 03, 05, 07 और 08 में Scope Playground, और Assignment Detector (07)। हर function call को whiteboard वाला अपना कमरा मिलता है; आप नामों को लिखे जाते, खोजे जाते और मिटते देख सकते हैं। अपने programs चलाने के लिए ✎ Edit code दबाएँ — results और errors असली Python जैसे ही हैं।

请留意这些互动框:LEGB Name Finder(第 02 节)、第 03、05、07 和 08 节中的 Scope Playground,以及 Assignment Detector(07)。每次函数调用都有一个带白板的 房间;你可以看到名字被写入、查找和销毁。点击 ✎ Edit code 运行你自己的程序 — 结果和错误都与真实 Python 一致。


SectionSection第 02 节

The LEGB Rule — Python's Lookup Order 🔍LEGB नियम — Python का खोजने का क्रम 🔍LEGB 规则 — Python 的查找顺序 🔍

Whenever Python reads a name, it searches four scopes in this exact order and stops at the first match. Learn the acronym LEGB and most scope confusion disappears.

जब भी Python कोई नाम पढ़ता है, वह ठीक इसी क्रम में चार scopes खोजता है और पहला मेल मिलते ही रुक जाता है। LEGB याद कर लें, तो scope की ज़्यादातर उलझन ख़त्म हो जाती है।

每当 Python 读取 一个名字时,它都会严格按这个顺序搜索四个作用域,找到第一个匹配就停下。记住缩写 LEGB,大部分作用域困惑就消失了。

01
L — Local: inside the current functionL — Local: मौजूदा function के अंदरL — 局部:当前函数内部
Names assigned in the function body, including its parameters. Checked first.
Function body में assign हुए नाम, उसके parameters समेत। सबसे पहले जाँचे जाते हैं।
在函数体内赋值的名字,包括形参。最先检查。
02
E — Enclosing: any outer function around this oneE — Enclosing: इसे घेरने वाला कोई बाहरी functionE — 外层:包住当前函数的外部函数
Only for nested functions. Python reaches outward one function at a time.
सिर्फ़ nested functions के लिए। Python एक-एक function बाहर की ओर जाता है।
只适用于嵌套函数。Python 一次向外一层地查找。
03
G — Global: the module (file) levelG — Global: module (file) का स्तरG — 全局:模块(文件)层
Names defined at the top level of the .py file, outside any function or class.
.py file के सबसे ऊपरी स्तर पर, किसी function या class के बाहर बने नाम।
在 .py 文件顶层、任何函数或类之外定义的名字。
04
B — Built-in: Python's own namesB — Built-in: Python के अपने नामB — 内置:Python 自带的名字
Names like print, len, range, str. Always available. Checked last.
print, len, range, str जैसे नाम। हमेशा मौजूद। सबसे आख़िर में जाँचे जाते हैं।
像 print、len、range、str 这样的名字。随时可用。最后检查。
# Global scope
x = "global-x"

def outer():
    x = "enclosing-x"              # Enclosing scope

    def inner():
        x = "local-x"              # Local scope
        print("inside inner:", x)   # finds Local first

    inner()
    print("inside outer:", x)       # finds Enclosing

outer()
print("module level: ", x)          # finds Global
print(len(x))                       # len comes from Built-in
OUTPUT
inside inner: local-x inside outer: enclosing-x module level: global-x 8
💡
Read It as "Local First, Built-in Last"इसे "पहले Local, आख़िर में Built-in" पढ़ें读作 "局部优先,内置最后"

When you can't work out which x a line is using, walk outward one ring at a time: current function → any function around it → top of the module → Python itself. The first place the name exists wins.

जब समझ न आए कि कोई line कौन-सा x इस्तेमाल कर रही है, तो एक-एक घेरा बाहर जाएँ: मौजूदा function → उसे घेरने वाला function → module का ऊपरी हिस्सा → Python ख़ुद। जहाँ नाम पहले मिले, वही जीतता है।

当你弄不清某一行用的是哪个 x 时,就一圈一圈往外走:当前函数 → 包住它的函数 → 模块顶层 → Python 本身。名字最先出现的地方胜出。

A LEGB Side Effect — Hiding a Built-inLEGB का एक असर — Built-in को छिपा देनाLEGB 的副作用 — 遮住内置名字

Because Global is checked before Built-in, a global name can hide a built-in one. Calling a variable list, str or sum is a classic beginner bug.

क्योंकि Global, Built-in से पहले जाँचा जाता है, इसलिए कोई global नाम built-in को छिपा सकता है। Variable का नाम list, str या sum रखना नए सीखने वालों का मशहूर bug है।

因为全局作用域在内置作用域 之前 被检查,全局名字可以遮住内置名字。把变量命名为 list、str 或 sum 是初学者的经典 bug。

list = ["a", "b"]              # a GLOBAL name now hides the built-in list

print(list)
try:
    letters = list("xyz")      # LEGB finds the global first!
except TypeError as e:
    print("TypeError:", e)

del list                       # remove the global name...
print(list("xyz"))             # ...and the built-in is visible again
OUTPUT
['a', 'b'] TypeError: 'list' object is not callable ['x', 'y', 'z']

Try It — LEGB Name Finderकरके देखें — LEGB Name Finder动手试试 — LEGB 名字查找器

Tick where x exists, choose where the print happens, and press Find it. Python searches the rings from the inside out and stops at the first match. Try printing from outer — the inner room is invisible from there.

जहाँ x मौजूद है वहाँ tick करें, चुनें कि print कहाँ होता है, और खोजें दबाएँ। Python घेरों को अंदर से बाहर की ओर खोजता है और पहला मेल मिलते ही रुकता है। outer से print करके देखें — वहाँ से अंदर का कमरा दिखता ही नहीं।

勾选 x 存在的位置,选择 print 发生在哪里,然后按 查找。Python 从内向外搜索各圈,找到 第一个 匹配就停下。试试在 outer 里打印 — 从那里看不到内层房间。

🔍 LEGB Name FinderLocal → Enclosing → Global → Built-in. First match wins.Local → Enclosing → Global → Built-in। पहला मेल जीतता है।局部 → 外层 → 全局 → 内置。第一个匹配胜出。
x exists in:x कहाँ है:x 存在于: print() is inside:कहाँ है:位于:
Try:आज़माएँ:试试:
Bbuilt-in
Gglobal (module)
Eouter() roomकमरा房间
Linner() roomकमरा房间

SectionSection第 03 节

Local Scope — What Happens Inside a Function Stays Inside 🚪Local Scope — Function के अंदर जो हुआ, अंदर ही रहता है 🚪局部作用域 — 函数内部的事留在内部 🚪

Any name that Python sees being assigned inside a function body is local to that function — unless you mark it global or nonlocal. This "local by default" rule is Python's most important scope decision.

Function body के अंदर जिस भी नाम को Python assign होते देखता है, वह उस function का local है — जब तक आप उसे global या nonlocal न लिखें। यह "default रूप से local" नियम Python का सबसे अहम scope फ़ैसला है।

凡是 Python 在函数体内看到被 赋值 的名字,都是这个函数的局部变量 — 除非你把它标为 global 或 nonlocal。这条 "默认局部" 规则是 Python 最重要的作用域决定。

def make_greeting():
    message = "Hello"              # local to make_greeting
    count   = 3                    # local to make_greeting
    print(message, count)

make_greeting()
print(message)                     # NameError — message does not exist here
OUTPUT
Hello 3 Traceback (most recent call last): File "demo.py", line 7, in <module> print(message) # NameError — message does not exist here ^^^^^^^ NameError: name 'message' is not defined

Each Call Gets a Fresh Local Scopeहर Call को नया Local Scope मिलता है每次调用都得到全新的局部作用域

def counter():
    n = 0                       # fresh n on every call
    n += 1
    print(f"n = {n}")

counter()
counter()
counter()
OUTPUT
n = 1 n = 1 n = 1
📈
Why Every Call Prints 1हर Call 1 क्यों Print करता है为什么每次调用都打印 1

The name n is born when counter() starts and destroyed when it returns. It has no memory between calls. If you want state that lasts across calls, you need a global, a class attribute or a closure — not a local variable.

नाम n counter() शुरू होते ही पैदा होता है और उसके लौटते ही नष्ट हो जाता है। Calls के बीच इसकी कोई याद नहीं रहती। अगर calls के पार टिकने वाली state चाहिए, तो global, class attribute या closure चाहिए — local variable नहीं।

名字 n 在 counter() 开始时诞生,在它返回时销毁。两次调用之间它没有任何记忆。如果你需要跨调用保留的状态,需要的是全局变量、类属性或闭包 — 而不是局部变量。

Try It — Scope Playgroundकरके देखें — Scope Playground动手试试 — 作用域游乐场

Step through real programs. Each function call opens a new room with its own whiteboard; the room is torn down when the call returns. Under each step you can see how Python searched for every name it read.

असली programs को कदम-दर-कदम चलाएँ। हर function call अपने whiteboard के साथ एक नया कमरा खोलता है; call लौटते ही कमरा हटा दिया जाता है। हर कदम के नीचे आप देख सकते हैं कि Python ने पढ़े गए हर नाम को कैसे खोजा।

一步步执行真实程序。每次函数调用都会打开一个带白板的新 房间;调用返回时房间被拆除。每一步下方都能看到 Python 是怎样查找它读取的每个名字的。

🏠 Scope PlaygroundEvery call gets a fresh room. Watch names appear and vanish.हर call को नया कमरा मिलता है। नामों को आते और गायब होते देखें।每次调用都有新房间。看名字出现又消失。
supports def, =, +=, global, nonlocal, return, printdef, =, +=, global, nonlocal, return, print चलते हैं支持 def、=、+=、global、nonlocal、return、print
compiler's decisions (made before running)compiler के फ़ैसले (चलाने से पहले)编译器的决定(运行前做出)

SectionSection第 04 节

Reading Globals from Inside a Function — Works Fine 📖Function के अंदर से Globals पढ़ना — ठीक चलता है 📖在函数内读取全局变量 — 完全没问题 📖

You can read a global variable from inside a function with no special syntax. Python's LEGB search finds it in the Global scope after failing to find it Locally. No error, no ceremony.

आप function के अंदर से global variable को बिना किसी ख़ास syntax के पढ़ सकते हैं। Python की LEGB खोज उसे Local में न पाकर Global scope में ढूँढ लेती है। न error, न कोई झंझट।

你可以在函数内部直接 读取 全局变量,不需要任何特殊语法。Python 的 LEGB 搜索在局部找不到后,会在全局作用域找到它。没有错误,没有麻烦。

APP_NAME = "PaymentService"       # global
VERSION  = "2.4.1"               # global

def show_banner():
    # Only READING globals — perfectly legal
    print(f"=== {APP_NAME} v{VERSION} ===")

show_banner()
show_banner()
OUTPUT
=== PaymentService v2.4.1 === === PaymentService v2.4.1 ===
✅
This Pattern Is Everywhereयह तरीका हर जगह है这种模式随处可见

Constants, imported modules, settings and helper functions defined at the top of a file are all globals that functions read freely. This is the normal, healthy use of globals — reading them, not writing them.

File के ऊपर बने constants, import किए modules, settings और helper functions — ये सब globals हैं जिन्हें functions खुलकर पढ़ते हैं। Globals का यही सामान्य, सही इस्तेमाल है — उन्हें पढ़ना, लिखना नहीं।

文件顶部定义的常量、导入的模块、配置和辅助函数都是全局名字,函数可以随意读取。这是全局变量正常、健康的用法 — 读取它们,而不是写入。


SectionSection第 05 节

The Famous Trap — UnboundLocalError 💥मशहूर जाल — UnboundLocalError 💥著名陷阱 — UnboundLocalError 💥

The One Line That Changes Everythingवह एक Line जो सब बदल देती है改变一切的那一行
You have a global counter. Your function reads it and prints it — works fine. Then you add one innocent line: counter += 1. Suddenly the whole function crashes with UnboundLocalError: cannot access local variable 'counter' where it is not associated with a value. (Python 3.10 and older said local variable 'counter' referenced before assignment.) You didn't touch the print line, so why is it broken?

This is one of the most confusing errors in Python. Understanding it teaches you how Python's compiler thinks — and once you see it, you can never un-see it.
आपके पास एक global counter है। आपका function उसे पढ़कर print करता है — ठीक चलता है। फिर आप एक मासूम-सी line जोड़ते हैं: counter += 1। अचानक पूरा function crash हो जाता है: UnboundLocalError: cannot access local variable 'counter' where it is not associated with a value। (Python 3.10 और पुराने में यह आता था: local variable 'counter' referenced before assignment।) आपने print वाली line छुई तक नहीं, तो वह क्यों टूटी?

यह Python की सबसे उलझाने वाली errors में से एक है। इसे समझना सिखाता है कि Python का compiler कैसे सोचता है — और एक बार देख लिया, तो फिर भूल नहीं सकते।
你有一个全局的 counter。函数读取并打印它 — 运行正常。然后你加了一行看似无害的代码:counter += 1。突然整个函数崩溃了:UnboundLocalError: cannot access local variable 'counter' where it is not associated with a value。(Python 3.10 及更早版本的说法是 local variable 'counter' referenced before assignment。)你根本没动 print 那一行,它为什么坏了?

这是 Python 中最让人困惑的错误之一。理解它,就能学会 Python 编译器的思考方式 — 一旦看懂,就再也忘不掉。
counter = 0                        # global

def bump():
    print("before:", counter)      # seemed fine before...
    counter += 1                    # ...but this line changes EVERYTHING
    print("after: ", counter)

bump()
OUTPUT
Traceback (most recent call last): File "demo.py", line 8, in <module> bump() ~~~~^^ File "demo.py", line 4, in bump print("before:", counter) # seemed fine before... ^^^^^^^ UnboundLocalError: cannot access local variable 'counter' where it is not associated with a value
⚠️
Why the Error Points at the print, Not the +=Error print पर क्यों है, += पर क्यों नहीं为什么错误指向 print 而不是 +=

When Python compiles the function, it scans the body once and marks every name that is assigned anywhere in it as local. It sees counter += 1 further down and decides counter is local for the whole function. Then the earlier print(counter) tries to read a local variable that has no value yet — and fails.

जब Python function को compile करता है, तो वह body को एक बार पढ़ता है और उसमें कहीं भी assign होने वाले हर नाम को local मान लेता है। वह नीचे counter += 1 देखता है और तय करता है कि counter पूरे function के लिए local है। फिर पहले वाला print(counter) ऐसा local variable पढ़ने की कोशिश करता है जिसकी अभी कोई value नहीं — और विफल होता है।

Python 编译 函数时,会把函数体扫描一遍,把在其中 任何地方 被赋值的名字都标记为局部。它在下面看到了 counter += 1,于是认定 counter 在整个函数中都是局部的。然后前面的 print(counter) 试图读取一个还没有值的局部变量 — 于是失败。

What Python Actually SeesPython असल में क्या देखता हैPython 实际看到的

🔎 Compile-Time Scan of bump()bump() की Compile-Time जाँच编译时对 bump() 的扫描
Step 1कदम 1第 1 步
Python scans the whole function body before running any of it. Python कुछ भी चलाने से पहले पूरी function body पढ़ता है। Python 在运行任何代码 之前,先扫描整个函数体。
Step 2कदम 2第 2 步
It sees counter += 1 — that is an assignment to counter. उसे counter += 1 दिखता है — यह counter को assignment है। 它看到 counter += 1 — 这是对 counter 的赋值。
Step 3कदम 3第 3 步
It marks counter as a local name for this function. वह counter को इस function का local नाम मान लेता है। 它把 counter 标记为这个函数的 局部 名字。
Step 4कदम 4第 4 步
Running starts. The first print tries to read the local counter. चलना शुरू होता है। पहला print local counter पढ़ने की कोशिश करता है। 开始运行。第一个 print 试图读取局部的 counter。
Step 5कदम 5第 5 步
The local counter has never been given a value. UnboundLocalError. Local counter को कभी value नहीं मिली। UnboundLocalError। 局部 counter 从未被赋值。UnboundLocalError。
🔧
The Rule to Rememberयाद रखने वाला नियम要记住的规则

If a function assigns to a name anywhere in its body, that name is local everywhere in that function — even on lines that come before the assignment. There is no half-local, half-global. It is local for the whole function.

अगर कोई function अपनी body में कहीं भी किसी नाम को assign करता है, तो वह नाम उस function में हर जगह local है — assignment से पहले वाली lines में भी। आधा local, आधा global कुछ नहीं होता। वह पूरे function के लिए local है।

如果一个函数在函数体的任何地方对某个名字 赋值,这个名字在整个函数中 处处 都是局部的 — 甚至在赋值之前的行里也是。没有一半局部、一半全局这回事。它在整个函数中都是局部的。

Try It — Watch the Trap Happenकरके देखें — जाल को होते देखें动手试试 — 看陷阱如何发生

Look at the compiler's decisions panel first: counter is already marked LOCAL because of the += line. Then step to the print and watch Python look in the local room, find it empty, and stop — it never checks the global room.

पहले compiler के फ़ैसले वाला हिस्सा देखें: += वाली line की वजह से counter पहले ही LOCAL मान लिया गया है। फिर print तक जाएँ और देखें कि Python local कमरे में देखता है, उसे ख़ाली पाता है, और रुक जाता है — वह global कमरा कभी नहीं जाँचता।

先看 编译器的决定 面板:因为 += 那一行,counter 已经被标为局部。然后一步步走到 print,看 Python 在局部房间里查找、发现是空的、然后停下 — 它从不检查全局房间。

💥 Scope Playground — The Trapजाल陷阱The compiler decides "local" before the first line runs.Compiler पहली line चलने से पहले ही "local" तय कर देता है।编译器在第一行运行前就决定了 "局部"。
supports def, =, +=, global, nonlocal, return, printdef, =, +=, global, nonlocal, return, print चलते हैं支持 def、=、+=、global、nonlocal、return、print
compiler's decisions (made before running)compiler के फ़ैसले (चलाने से पहले)编译器的决定(运行前做出)

SectionSection第 06 节

The global Keyword — Reaching Out to Change Globals 🌍global Keyword — Globals बदलने के लिए बाहर पहुँचना 🌍global 关键字 — 向外修改全局变量 🌍

To really change a global variable from inside a function, declare it global at the top of the function. This tells Python: "don't make a local counter — use the module-level one".

Function के अंदर से global variable को सच में बदलने के लिए function के ऊपर उसे global घोषित करें। यह Python से कहता है: "local counter मत बनाओ — module-level वाला इस्तेमाल करो"।

要在函数内部真正修改全局变量,就在函数开头把它声明为 global。这是在告诉 Python:"不要创建局部的 counter — 用模块级的那个"。

counter = 0                        # global

def bump():
    global counter               # "I mean THE module-level counter"
    print("before:", counter)
    counter += 1                    # now modifies the global
    print("after: ", counter)

bump()
bump()
bump()
print("final: ", counter)
OUTPUT
before: 0 after: 1 before: 1 after: 2 before: 2 after: 3 final: 3

Before vs After — What global Really Changesपहले बनाम बाद — global असल में क्या बदलता है之前与之后 — global 真正改变了什么

🚫 Without globalglobal के बिना没有 global
Behaviourव्यवहार行为What happensक्या होता है结果
AssignmentAssignment赋值Creates a local variable with the same nameउसी नाम का local variable बनाता है创建一个同名的局部变量
Reading it before the assignmentAssignment से पहले पढ़ना在赋值之前读取UnboundLocalError
Module stateModule की state模块状态Unchanged after the function returnsFunction लौटने के बाद भी नहीं बदली函数返回后保持不变
Bug riskBug का ख़तराbug 风险High — hidden shadowingज़्यादा — छिपा हुआ shadowing高 — 隐蔽的遮蔽
✅ With globalglobal के साथ有 global
Behaviourव्यवहार行为What happensक्या होता है结果
AssignmentAssignment赋值Changes the module-level variableModule-level variable बदलता है修改模块级变量
Reading itउसे पढ़ना读取Reads the same global — no errorवही global पढ़ता है — कोई error नहीं读取同一个全局变量 — 没有错误
Module stateModule की state模块状态Lasts across all future callsआगे के सभी calls तक बनी रहती है在之后所有调用中保持
Bug riskBug का ख़तराbug 风险Explicit — the reader knows what changesसाफ़ — पढ़ने वाला जानता है क्या बदलता है明确 — 读者知道改了什么
🚩
Use global Sparinglyglobal कम से कम इस्तेमाल करें谨慎使用 global

Every function that changes a global becomes a hidden link to every other function that reads it. Testing gets hard, threads get risky and bugs get hard to trace. Prefer returning values, passing arguments or using a class. Keep global for real module-wide state — settings flags, caches — not for general talk between functions.

Global बदलने वाला हर function उसे पढ़ने वाले हर दूसरे function से एक छिपा रिश्ता बना लेता है। Testing मुश्किल होती है, threads जोखिम भरे होते हैं और bugs ढूँढना कठिन। Values लौटाना, arguments देना या class इस्तेमाल करना बेहतर है। global को असली module-wide state — settings flags, caches — के लिए रखें, functions के बीच आम बातचीत के लिए नहीं।

每个修改全局变量的函数,都会和所有读取它的函数之间形成隐藏的联系。测试变难,多线程有风险,bug 难以追踪。更好的做法是返回值、传参或使用类。把 global 留给真正的模块级状态 — 配置开关、缓存 — 而不是函数之间的日常通信。


SectionSection第 07 节

The Mutation Loophole — Why Lists and Dicts Seem to "Just Work" 🛒Mutation की छूट — Lists और Dicts "बस चल" क्यों जाते हैं 🛒修改的漏洞 — 为什么 list 和 dict 似乎 "直接就能用" 🛒

Here is something that trips up almost everyone. This code has no global declaration, and yet it works:

यह बात लगभग सबको उलझाती है। इस code में कोई global घोषणा नहीं है, फिर भी यह चलता है:

下面这一点几乎让所有人犯迷糊。这段代码 没有 global 声明,却照样能运行:

cart = []                            # global list

def add_item(item):
    cart.append(item)                # no 'global cart' needed?!

add_item("book")
add_item("pen")
print(cart)
OUTPUT
['book', 'pen']
🎯
The Fine Line: Mutating vs Rebindingबारीक रेखा: Mutate करना बनाम Rebind करना细微差别:修改与重新绑定

cart.append(x) does not assign to cart — it changes the object that cart already points to. Python's scope rules only care about assignment to a name. Reading a name and calling a method on it is not an assignment, so no local copy of the name is made, and the global object changes.

cart.append(x) cart को assign नहीं करता — यह उस object को बदलता है जिसकी ओर cart पहले से इशारा करता है। Python के scope नियम सिर्फ़ नाम को assignment देखते हैं। नाम पढ़कर उस पर method चलाना assignment नहीं है, इसलिए नाम का कोई local रूप नहीं बनता, और global object बदल जाता है।

cart.append(x) 并没有给 cart 赋值 — 它修改的是 cart 已经指向的对象。Python 的作用域规则只关心 对名字的赋值。读取名字并调用它的方法不算赋值,所以不会产生局部名字,全局对象就被修改了。

cart = []

def reset_cart():
    cart = []                        # this is REBINDING -> creates a local!
    cart.append("fresh")

reset_cart()
print("global cart:", cart)         # still []
OUTPUT
global cart: []

Try It — Mutation vs Rebindingकरके देखें — Mutation बनाम Rebinding动手试试 — 修改与重新绑定

Compare the tabs. With .append() or d[k] = v the name is only read and the global object changes. With cart = [] a new local name appears and the global list is left alone.

Tabs की तुलना करें। .append() या d[k] = v में नाम सिर्फ़ पढ़ा जाता है और global object बदलता है। cart = [] में एक नया local नाम आता है और global list अछूती रहती है।

比较这几个标签页。用 .append() 或 d[k] = v 时,名字只是被 读取,全局对象被修改。用 cart = [] 时,出现了一个 新的局部 名字,全局 list 保持不变。

🛒 Scope Playground — Mutate vs RebindChanging an object is not assigning a name.Object बदलना, नाम को assign करना नहीं है।修改对象不等于给名字赋值。
supports def, =, +=, global, nonlocal, return, printdef, =, +=, global, nonlocal, return, print चलते हैं支持 def、=、+=、global、nonlocal、return、print
compiler's decisions (made before running)compiler के फ़ैसले (चलाने से पहले)编译器的决定(运行前做出)

Try It — Assignment Detectorकरके देखें — Assignment Detector动手试试 — 赋值检测器

Type one line to put inside a function (use ; for two lines). The detector answers the three questions from the table below, then runs the program for real. Don't miss lst += [4] — it looks like a mutation, but it is an assignment.

Function के अंदर रखने के लिए एक line लिखें (दो lines के लिए ; लगाएँ)। Detector नीचे की table वाले तीन सवालों का जवाब देता है, फिर program सच में चलाता है। lst += [4] ज़रूर आज़माएँ — यह mutation जैसा दिखता है, पर यह assignment है।

输入一行要放进函数里的代码(两行用 ; 分隔)。检测器会回答下表中的三个问题,然后真正运行程序。别错过 lst += [4] — 它看起来像修改,其实是赋值。

🔎 Assignment DetectorIs it assignment? Does it make a local? Does the global change?क्या यह assignment है? क्या local बनता है? क्या global बदलता है?是赋值吗?会产生局部变量吗?全局会改变吗?
Try:आज़माएँ:试试:
Operation inside the functionFunction के अंदर काम函数内的操作Assignment to a name?नाम को assignment?是对名字赋值吗?Effect on the globalGlobal पर असर对全局的影响Needs global?global चाहिए?需要 global?
x = 5Yesहाँ是None — creates a local xकोई नहीं — local x बनता है无 — 创建局部 xYes, to change the globalहाँ, global बदलने के लिए要改全局就需要
x += 1Yes (rebind)हाँ (rebind)是(重新绑定)x is local, so reading it fails: UnboundLocalErrorx local है, इसलिए पढ़ना विफल: UnboundLocalErrorx 是局部的,读取失败:UnboundLocalErrorYes, to change the globalहाँ, global बदलने के लिए要改全局就需要
lst.append(v)No — method callनहीं — method call否 — 方法调用Yes — changes the object in placeहाँ — object उसी जगह बदलता है是 — 原地修改对象Noनहीं否
d[k] = vNo — item assignmentनहीं — item assignment否 — 元素赋值Yes — changes the object in placeहाँ — object उसी जगह बदलता है是 — 原地修改对象Noनहीं否
lst = [1, 2]Yes (rebind)हाँ (rebind)是(重新绑定)None — creates a local lstकोई नहीं — local lst बनता है无 — 创建局部 lstYes, to change the globalहाँ, global बदलने के लिए要改全局就需要
lst += [4]Yes — looks like a mutation, but rebindsहाँ — mutation जैसा दिखता है, पर rebind करता है是 — 看似修改,实为重新绑定UnboundLocalError (lst is local)UnboundLocalError (lst local है)UnboundLocalError(lst 是局部的)Yes — or use lst.extend([4])हाँ — या lst.extend([4])需要 — 或改用 lst.extend([4])
print(x)No — read onlyनहीं — सिर्फ़ पढ़ना否 — 只读Reads the globalGlobal पढ़ता है读取全局Noनहीं否

SectionSection第 08 节

Enclosing Scope and the nonlocal Keyword 🪆Enclosing Scope और nonlocal Keyword 🪆外层作用域与 nonlocal 关键字 🪆

When one function is defined inside another, the outer function's variables form an enclosing scope for the inner one. The inner function can read them freely, but assigning makes a new local name — the same trap as with globals. To really change the enclosing variable, use nonlocal.

जब एक function दूसरे के अंदर बनता है, तो बाहरी function के variables अंदर वाले के लिए enclosing scope बनाते हैं। अंदर वाला function उन्हें आराम से पढ़ सकता है, पर assign करने पर नया local नाम बन जाता है — globals वाला ही जाल। Enclosing variable को सच में बदलने के लिए nonlocal इस्तेमाल करें।

当一个函数定义在另一个函数内部时,外层函数的变量就构成了内层函数的 外层 作用域。内层函数可以随意读取它们,但一赋值就会产生新的局部名字 — 和全局变量是同一个陷阱。要真正修改外层变量,请用 nonlocal。

def make_counter():
    count = 0                       # enclosing variable

    def increment():
        nonlocal count             # reach outward to enclosing scope
        count += 1
        return count

    return increment

tick = make_counter()
print(tick())
print(tick())
print(tick())
OUTPUT
1 2 3
KeywordKeyword关键字Target scopeकिस scope पर目标作用域Typical useआम इस्तेमाल典型用途
globalThe module (top) levelModule (सबसे ऊपरी) स्तर模块(顶层)Change a module-level variable from any functionकिसी भी function से module-level variable बदलना在任何函数中修改模块级变量
nonlocalThe nearest enclosing function that has the nameउस नाम वाला सबसे पास का enclosing function拥有这个名字的最近一层外围函数Change an outer function's variable from a nested one (closures, decorators)Nested function से बाहरी function का variable बदलना (closures, decorators)在嵌套函数中修改外层函数的变量(闭包、装饰器)
🔑
This Is How Closures WorkClosures ऐसे ही काम करते हैं这就是闭包的工作原理

The tick function above remembers count from make_counter even after make_counter has returned. That is a closure: an inner function plus the enclosing scope it captured. It is the base of decorators, callbacks and many functional patterns in Python.

ऊपर वाला tick function make_counter के लौट जाने के बाद भी उसका count याद रखता है। यही closure है: एक inner function और वह enclosing scope जिसे उसने पकड़ रखा है। Python में decorators, callbacks और कई functional तरीकों की नींव यही है।

上面的 tick 函数在 make_counter 返回之后,仍然 记得 它的 count。这就是 闭包:一个内层函数加上它捕获的外层作用域。它是 Python 中装饰器、回调和许多函数式模式的基础。

Try It — Closures and nonlocalकरके देखें — Closures और nonlocal动手试试 — 闭包与 nonlocal

Watch make_counter()'s room: normally it is destroyed when the call ends, but increment keeps using it. Every tick() writes to that same enclosing room. The two counters tab shows that each call to make_counter() gets its own separate room.

make_counter() का कमरा देखें: आम तौर पर call ख़त्म होते ही वह नष्ट हो जाता, पर increment उसे इस्तेमाल करता रहता है। हर tick() उसी enclosing कमरे में लिखता है। two counters tab दिखाता है कि make_counter() के हर call को अपना अलग कमरा मिलता है।

观察 make_counter() 的房间:通常调用结束时它就被销毁了,但 increment 还在用它。每次 tick() 都写入同一个外层房间。two counters 标签页显示,每次调用 make_counter() 都会得到各自独立的房间。

🔄 Scope Playground — ClosuresClosures闭包Inner functions keep their enclosing room alive.Inner functions अपने enclosing कमरे को ज़िंदा रखते हैं।内层函数让外层房间一直存活。
supports def, =, +=, global, nonlocal, return, printdef, =, +=, global, nonlocal, return, print चलते हैं支持 def、=、+=、global、nonlocal、return、print
compiler's decisions (made before running)compiler के फ़ैसले (चलाने से पहले)编译器的决定(运行前做出)

SectionSection第 09 节

No Block Scope — Loops, if and Comprehensions 🔁Block Scope नहीं — Loops, if और Comprehensions 🔁没有块作用域 — 循环、if 与推导式 🔁

Many languages give each for loop or if block its own scope. Python does not. Only functions (and classes and modules) make a new scope. A loop variable stays alive after the loop. The one exception you will meet often: a list, set or dict comprehension has its own small scope.

कई भाषाएँ हर for loop या if block को अपना scope देती हैं। Python नहीं देता। सिर्फ़ functions (और classes व modules) नया scope बनाते हैं। Loop का variable loop के बाद भी ज़िंदा रहता है। एक अपवाद जो आपको अक्सर मिलेगा: list, set या dict comprehension का अपना छोटा scope होता है।

许多语言会给每个 for 循环或 if 块一个独立的作用域。Python 不会。只有函数(以及类和模块)会创建新的作用域。循环变量在循环结束后依然存在。你经常会遇到的一个例外是:list、set 或 dict 推导式 有自己的小作用域。

# Loops and if-blocks do NOT create a new scope
for i in range(3):
    square = i * i

print("i after the loop:", i)          # 2 — still alive
print("square after the loop:", square) # 4

if True:
    inside_if = "visible"
print(inside_if)                        # visible — no block scope

# But a comprehension DOES have its own scope
nums = [k * 10 for k in range(3)]
print(nums)
try:
    print(k)
except NameError as e:
    print("NameError:", e)
OUTPUT
i after the loop: 2 square after the loop: 4 visible [0, 10, 20] NameError: name 'k' is not defined
⚠️
Two Practical Resultsदो व्यावहारिक नतीजे两个实际影响

A name you create inside an if exists only if that branch ran — reading it otherwise raises NameError. And a loop variable can quietly overwrite a variable with the same name that you defined earlier in the same function or module.

if के अंदर बना नाम तभी मौजूद होता है जब वह शाखा चली हो — वरना उसे पढ़ने पर NameError आता है। और loop का variable उसी function या module में पहले बने उसी नाम के variable को चुपचाप बदल सकता है।

在 if 里创建的名字,只有那个分支执行过才存在 — 否则读取它会抛出 NameError。另外,循环变量可能会悄悄覆盖同一函数或模块中先前定义的同名变量。


SectionSection第 10 节

Golden Rules 🏆सुनहरे नियम 🏆黄金法则 🏆

🌐 Function Scope — Rules to RememberFunction Scope — याद रखने वाले नियम函数作用域 — 必须记住的规则
1
LEGB — Local, Enclosing, Global, Built-in. Python looks up every name it reads in that order. When you can't tell which variable is which, walk the rings outward one at a time.
LEGB — Local, Enclosing, Global, Built-in। Python हर पढ़े गए नाम को इसी क्रम में खोजता है। जब समझ न आए कि कौन-सा variable कौन है, तो एक-एक घेरा बाहर जाएँ।
LEGB — 局部、外层、全局、内置。Python 按这个顺序查找它读取的每个名字。分不清哪个变量是哪个时,就一圈一圈往外走。
2
Assignment inside a function makes the name local for the whole function — including lines before the assignment. That is why UnboundLocalError: cannot access local variable ... where it is not associated with a value happens even when the read is above the write.
Function के अंदर assignment नाम को पूरे function के लिए local बना देता है — assignment से पहले वाली lines समेत। इसीलिए पढ़ना ऊपर और लिखना नीचे हो, तब भी UnboundLocalError: cannot access local variable ... where it is not associated with a value आता है।
函数内的赋值会让这个名字在整个函数中都是局部的 — 包括赋值之前的行。所以即使读取写在赋值上面,也会出现 UnboundLocalError: cannot access local variable ... where it is not associated with a value。
3
Reading a global is free; writing to one is not. You can read constants and settings from inside any function. But to reassign a global, declare it global at the top of the function.
Global पढ़ना मुफ़्त है; उसमें लिखना नहीं। आप किसी भी function के अंदर से constants और settings पढ़ सकते हैं। पर global को दोबारा assign करने के लिए function के ऊपर उसे global घोषित करें।
读取全局变量是免费的;写入则不是。你可以在任何函数里读取常量和配置。但要 重新赋值 全局变量,就要在函数开头声明 global。
4
Use global only for real module-wide state (settings, caches). For general communication between functions, return values and pass arguments instead. Changeable globals make code hard to test and reason about.
global सिर्फ़ असली module-wide state (settings, caches) के लिए इस्तेमाल करें। Functions के बीच आम बातचीत के लिए values लौटाएँ और arguments दें। बदलने वाले globals code को test करना और समझना कठिन बनाते हैं।
只在真正的模块级状态(配置、缓存)中使用 global。函数之间的一般通信,请 返回值并传递参数。可变的全局变量让代码难以测试和理解。
5
Changing an object (lst.append, d[k] = v) is not assignment — it does not create a local name. That is why you don't need global to change a global list or dict in place. But watch out: lst += [4] is an assignment.
Object बदलना (lst.append, d[k] = v) assignment नहीं है — इससे local नाम नहीं बनता। इसीलिए global list या dict को उसी जगह बदलने के लिए global नहीं चाहिए। पर ध्यान दें: lst += [4] एक assignment है।
修改对象(lst.append、d[k] = v)不是赋值 — 它不会产生局部名字。所以原地修改全局 list 或 dict 不需要 global。但要当心:lst += [4] 是 赋值。
6
Use nonlocal to change an enclosing function's variable from a nested function. This is how closures with changing state (counters, caches, simple decorators) are built.
Nested function से enclosing function का variable बदलने के लिए nonlocal इस्तेमाल करें। बदलती state वाले closures (counters, caches, सरल decorators) ऐसे ही बनते हैं।
要在嵌套函数中修改外层函数的变量,请用 nonlocal。带可变状态的 闭包(计数器、缓存、简单装饰器)就是这样构建的。
7
Loops and if blocks do not create a scope. Only functions, classes and modules do (plus comprehensions). A loop variable is still alive after the loop.
Loops और if blocks scope नहीं बनाते। सिर्फ़ functions, classes और modules बनाते हैं (और comprehensions)। Loop का variable loop के बाद भी ज़िंदा रहता है।
循环和 if 块不会创建作用域。只有函数、类和模块会(另外还有推导式)。循环变量在循环结束后仍然存在。
8
Never name a variable after a built-in such as list, str, sum or id. LEGB finds your name first and the built-in becomes unreachable.
Variable का नाम कभी built-in पर न रखें, जैसे list, str, sum या id। LEGB पहले आपका नाम ढूँढ लेता है और built-in तक पहुँचना बंद हो जाता है।
永远不要用内置名字给变量命名,例如 list、str、sum 或 id。LEGB 会先找到你的名字,内置的就用不了了。
9
When a scope bug confuses you, ask two questions: (1) Am I assigning to the name, or only reading it? (2) Which scope did Python actually pick? Answer those and the bug shows itself.
जब scope का bug उलझाए, तो दो सवाल पूछें: (1) क्या मैं नाम को assign कर रहा हूँ, या सिर्फ़ पढ़ रहा हूँ? (2) Python ने असल में कौन-सा scope चुना? इनका जवाब दें और bug ख़ुद सामने आ जाएगा।
被作用域 bug 困住时,问自己两个问题:(1) 我是在给这个名字赋值,还是只是读取?(2) Python 实际选了哪个作用域?回答了这两个问题,bug 就会现身。

Quick Checkछोटी जाँच小测验

Seven questions on scope. Pick an answer to see why.

Scope पर सात सवाल। जवाब चुनें और कारण देखें।

关于作用域的七道题。选择答案查看解释。

🎯
The Takeawayमुख्य सीख要点总结

Python reads names in LEGB order and decides "local or not" at compile time: any assignment inside a function makes that name local for the whole function. Read globals freely, change them only with global (or better, return values), use nonlocal for closures, and remember that changing an object is not the same as assigning a name.

Python नामों को LEGB क्रम में पढ़ता है और compile के समय तय करता है कि "local है या नहीं": function के अंदर कोई भी assignment उस नाम को पूरे function के लिए local बना देता है। Globals खुलकर पढ़ें, उन्हें सिर्फ़ global से बदलें (या बेहतर, values लौटाएँ), closures के लिए nonlocal इस्तेमाल करें, और याद रखें कि object बदलना, नाम को assign करने जैसा नहीं है।

Python 按 LEGB 顺序读取名字,并在编译时决定 "是否局部":函数内的任何赋值都会让这个名字在整个函数中成为局部的。随意读取全局变量,只用 global 修改它们(更好的做法是返回值),用 nonlocal 实现闭包,并记住:修改对象不等于给名字赋值。

🐍 Try Python Yourself

Write, run and visualize Python step by step. No install needed.