Why Scope Exists — The Rooms in Your House 🏠Scope क्यों होता है — आपके घर के कमरे 🏠为什么需要作用域 — 你家里的房间 🏠
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.Python functions इन्हीं कमरों जैसे काम करते हैं। हर function call का अपना निजी whiteboard होता है — एक local scope। वहाँ लिखे नाम दूसरे कमरों में नहीं रिसते। यही 50,000 lines के codebase को सँभाले रखता है: दो functions दोनों
i नाम का variable रख सकते हैं, एक-दूसरे के रास्ते में आए बिना।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 按固定顺序查看各个作用域,来决定一个名字对应哪个值。懂得这个顺序,就是 "代码能跑" 和 "我不知道代码为什么能跑" 之间的区别。
i, No Clashi नाम के तीन Variables, कोई टकराव नहीं三个都叫 i 的变量,互不冲突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。
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 都会为这次调用创建一个全新的 局部命名空间,并在函数返回时丢弃它。函数内部创建的名字随这次调用诞生和消亡 — 除非你另有说明。
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 一致。
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,大部分作用域困惑就消失了。
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
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
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 里打印 — 从那里看不到内层房间。
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
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()
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 是怎样查找它读取的每个名字的。
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()
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 का यही सामान्य, सही इस्तेमाल है — उन्हें पढ़ना, लिखना नहीं।
文件顶部定义的常量、导入的模块、配置和辅助函数都是全局名字,函数可以随意读取。这是全局变量正常、健康的用法 — 读取它们,而不是写入。
The Famous Trap — UnboundLocalError 💥मशहूर जाल — UnboundLocalError 💥著名陷阱 — UnboundLocalError 💥
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.
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()
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 实际看到的
bump()bump() की Compile-Time जाँच编译时对 bump() 的扫描counter += 1 — that is an assignment to counter.
उसे counter += 1 दिखता है — यह counter को assignment है।
它看到 counter += 1 — 这是对 counter 的赋值。
counter as a local name for this function.
वह counter को इस function का local नाम मान लेता है।
它把 counter 标记为这个函数的 局部 名字。
print tries to read the local counter.
चलना शुरू होता है। पहला print local counter पढ़ने की कोशिश करता है।
开始运行。第一个 print 试图读取局部的 counter。
counter has never been given a value. UnboundLocalError.
Local counter को कभी value नहीं मिली। UnboundLocalError।
局部 counter 从未被赋值。UnboundLocalError。
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 在局部房间里查找、发现是空的、然后停下 — 它从不检查全局房间。
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)
Before vs After — What global Really Changesपहले बनाम बाद — global असल में क्या बदलता है之前与之后 — global 真正改变了什么
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高 — 隐蔽的遮蔽 |
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साफ़ — पढ़ने वाला जानता है क्या बदलता है明确 — 读者知道改了什么 |
global Sparinglyglobal कम से कम इस्तेमाल करें谨慎使用 globalEvery 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 留给真正的模块级状态 — 配置开关、缓存 — 而不是函数之间的日常通信。
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)
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 []
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 保持不变。
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] — 它看起来像修改,其实是赋值。
| Operation inside the functionFunction के अंदर काम函数内的操作 | Assignment to a name?नाम को assignment?是对名字赋值吗? | Effect on the globalGlobal पर असर对全局的影响 | Needs global?global चाहिए?需要 global? |
|---|---|---|---|
x = 5 | Yesहाँ是 | None — creates a local xकोई नहीं — local x बनता है无 — 创建局部 x | Yes, to change the globalहाँ, global बदलने के लिए要改全局就需要 |
x += 1 | Yes (rebind)हाँ (rebind)是(重新绑定) | x is local, so reading it fails: UnboundLocalErrorx local है, इसलिए पढ़ना विफल: UnboundLocalErrorx 是局部的,读取失败:UnboundLocalError | Yes, to change the globalहाँ, global बदलने के लिए要改全局就需要 |
lst.append(v) | No — method callनहीं — method call否 — 方法调用 | Yes — changes the object in placeहाँ — object उसी जगह बदलता है是 — 原地修改对象 | Noनहीं否 |
d[k] = v | No — 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 बनता है无 — 创建局部 lst | Yes, 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नहीं否 |
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())
| KeywordKeyword关键字 | Target scopeकिस scope पर目标作用域 | Typical useआम इस्तेमाल典型用途 |
|---|---|---|
global | The module (top) levelModule (सबसे ऊपरी) स्तर模块(顶层) | Change a module-level variable from any functionकिसी भी function से module-level variable बदलना在任何函数中修改模块级变量 |
nonlocal | The 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)在嵌套函数中修改外层函数的变量(闭包、装饰器) |
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() 都会得到各自独立的房间。
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)
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。另外,循环变量可能会悄悄覆盖同一函数或模块中先前定义的同名变量。
Golden Rules 🏆सुनहरे नियम 🏆黄金法则 🏆
UnboundLocalError: cannot access local variable ... where it is not associated with a value happens even when the read is above the write.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。global at the top of the function.global घोषित करें।global。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。函数之间的一般通信,请 返回值并传递参数。可变的全局变量让代码难以测试和理解。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.lst.append, d[k] = v) assignment नहीं है — इससे local नाम नहीं बनता। इसीलिए global list या dict को उसी जगह बदलने के लिए global नहीं चाहिए। पर ध्यान दें: lst += [4] एक assignment है।lst.append、d[k] = v)不是赋值 — 它不会产生局部名字。所以原地修改全局 list 或 dict 不需要 global。但要当心:lst += [4] 是 赋值。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.nonlocal इस्तेमाल करें। बदलती state वाले closures (counters, caches, सरल decorators) ऐसे ही बनते हैं।nonlocal。带可变状态的 闭包(计数器、缓存、简单装饰器)就是这样构建的。if blocks do not create a scope. Only functions, classes and modules do (plus comprehensions). A loop variable is still alive after the loop.if blocks scope नहीं बनाते। सिर्फ़ functions, classes और modules बनाते हैं (और comprehensions)। Loop का variable loop के बाद भी ज़िंदा रहता है।if 块不会创建作用域。只有函数、类和模块会(另外还有推导式)。循环变量在循环结束后仍然存在。list, str, sum or id. LEGB finds your name first and the built-in becomes unreachable.list, str, sum या id। LEGB पहले आपका नाम ढूँढ लेता है और built-in तक पहुँचना बंद हो जाता है।list、str、sum 或 id。LEGB 会先找到你的名字,内置的就用不了了。Quick Checkछोटी जाँच小测验
Seven questions on scope. Pick an answer to see why.
Scope पर सात सवाल। जवाब चुनें और कारण देखें।
关于作用域的七道题。选择答案查看解释。
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 实现闭包,并记住:修改对象不等于给名字赋值。