कोर्स
कुछ ही पंक्तियाँ लिखने के बाद “name is not defined” या “local variable referenced before assignment” देखना निराशाजनक होता है। कोड ठीक दिखता है, फिर भी Python असहमत है। जड़ कारण लगभग हमेशा स्कोप होता है: किसी नाम की दृश्यता कहाँ तक है और उस क्षण वह किस ऑब्जेक्ट को संदर्भित करता है।
एक बार जब आप Python के नाम लुकअप—जिसे अक्सर LEGB नियम के रूप में संक्षेपित किया जाता है—को समझ लेते हैं, तो ये त्रुटियाँ पूर्वानुमेय और आसान हो जाती हैं। इस ट्यूटोरियल में, मैं Python में वेरिएबल और स्कोप को चरण-दर-चरण समझाऊंगा।
वेरिएबल क्या है?
वेरिएबल एक नाम है जो किसी ऑब्जेक्ट से बंधा होता है। Python में, आप असाइनमेंट ऑपरेटर = से बाइंडिंग बनाते हैं। नाम टाइप्ड नहीं होते; वे किसी भी ऑब्जेक्ट (string, int, list, function, आदि) की ओर इशारा कर सकते हैं।
customer_name = "Ava Torres"
order_count = 1
total_cents = 1 + 2 * 300 # evaluated first, then bound
सिंटैक्स त्रुटियों और सूक्ष्म बग्स से बचने के लिए वेरिएबल का नामकरण करते समय इन नियमों और परंपराओं का पालन करें।
- केवल अक्षर, अंक और अंडरस्कोर का उपयोग करें; नाम अंक से शुरू नहीं हो सकते।
True,for, याclassजैसे कीवर्ड को नाम के रूप में उपयोग न करें।
list,dict,sum, याmaxजैसे बिल्ट-इन को शैडो करने से बचें।
- पठनीयता के लिए
snake_caseको प्राथमिकता दें (PEP 8)।
ये उदाहरण क्या नहीं करना है और Python कौन-सी त्रुटियाँ उठाता है, यह दिखाते हैं:
first string value = "First string" # spaces not allowed
# SyntaxError: invalid syntax
1st_value = 10 # cannot start with a digit
# SyntaxError: invalid decimal literal
True = "yes" # cannot assign to a keyword
# SyntaxError: cannot assign to True
Python स्कोप कैसे काम करता है (LEGB)
स्कोप वह क्षेत्र है जहाँ कोई नाम दृश्य होता है। जब आप कोई नाम उपयोग करते हैं, तो Python इसे इस क्रम में खोजता है (LEGB):
- Local: वर्तमान फ़ंक्शन का स्कोप।
- Enclosing: किसी भी बाहरी फ़ंक्शन का स्कोप (नेस्टेड फ़ंक्शन के लिए)।
- Global: मॉड्यूल का शीर्ष स्तर (मॉड्यूल नेमस्पेस)।
- Built-in: Python द्वारा
builtinsमें परिभाषित नाम (उदाहरण के लिए,len,print)।
नाम नेमस्पेसेज़ में रहते हैं (इन्हें नामों से ऑब्जेक्ट तक का मैपिंग करने वाली डिक्शनरी समझें)। स्कोप इस बारे में है कि कोड के किसी बिंदु पर नाम को हल करने के लिए Python किन नेमस्पेसेज़ से परामर्श करता है।
लोकल स्कोप
किसी फ़ंक्शन के अंदर असाइन किए गए नाम, जब तक अलग से घोषित न किए जाएँ, उस फ़ंक्शन के लिए लोकल होते हैं। वे फ़ंक्शन के बाहर दृश्य नहीं होते।
def show_order_id():
order_id = 42
print("inside function:", order_id)
show_order_id()
print("outside function:", order_id) # NameError
Python कंपाइल टाइम पर स्कोप तय करता है। यदि किसी फ़ंक्शन में उसके बॉडी के कहीं भी किसी नाम को असाइन किया जाता है, तो उस फ़ंक्शन में उस नाम के सभी उपयोग लोकल माने जाते हैं—जिससे आप असाइनमेंट से पहले पढ़ेंगे तो सामान्य UnboundLocalError मिल सकता है:
discount_rate = 0.10 # module-level (global)
def price_with_discount(amount_cents):
print("configured discount:", discount_rate) # looks local because of the assignment below
discount_rate = 0.20 # assignment makes 'discount_rate' local in this function
return int(amount_cents * (1 - discount_rate))
# UnboundLocalError: cannot access local variable 'discount_rate' where it is not associated with a value
फ़ंक्शन में मॉड्यूल-स्तरीय नाम का उपयोग करने के लिए, या तो उसे असाइन करने से बचें या उसे global के रूप में चिह्नित करें (नीचे कवर किया गया)।
एनक्लोज़िंग स्कोप (क्लोज़र्स)
नेस्टेड फ़ंक्शंस अपने तुरंत घेरने वाले फ़ंक्शन के नाम देख सकते हैं। ऐसे नाम को फिर से बाँधने (सिर्फ पढ़ने नहीं) के लिए, उसे nonlocal घोषित करें।
def make_step_counter():
count = 0 # enclosing scope for 'increment'
def increment():
nonlocal count # rebind the 'count' in the nearest enclosing function
count += 1
return count
return increment
step = make_step_counter()
print(step()) # 1
print(step()) # 2
nonlocal के बिना, increment() के अंदर count को असाइन करना एक नया लोकल नाम बना देता और बाहरी count अपरिवर्तित रहता।
ग्लोबल स्कोप
मॉड्यूल के शीर्ष स्तर पर असाइन किए गए नाम मॉड्यूल के ग्लोबल नेमस्पेस में रहते हैं। कोई भी फ़ंक्शन उन्हें पढ़ सकता है। किसी फ़ंक्शन के अंदर से मॉड्यूल-स्तरीय नाम को असाइन करने के लिए, उसे global घोषित करें।
greeting = "Hello"
def greet_city(city_name):
print(greeting, city_name) # reads global
def set_greeting(new_greeting):
global greeting
greeting = new_greeting # rebinds global
greet_city("Nairobi") # Hello Nairobi
set_greeting("Hi")
greet_city("Nairobi") # Hi Nairobi
global का उपयोग संयम से करें। कोड को टेस्टेबल और पूर्वानुमेय बनाए रखने के लिए मानों को पैरामीटर के रूप में पास करना और परिणाम लौटाना बेहतर है।
बिल्ट-इन स्कोप (और कीवर्ड्स पर एक टिप्पणी)
बिल्ट-इन स्कोप में len, print, और Exception जैसे नाम होते हैं। इन्हें शैडो करने से बचें, वरना उस स्कोप में आप बिल्ट-इन तक पहुँच खो देंगे।
list = [1, 2, 3] # shadows the built-in 'list' constructor
list("abc") # TypeError: 'list' object is not callable
del list # fix by deleting the shadowing name
कीवर्ड्स (जैसे if, for, def) Python के सिंटैक्स का हिस्सा हैं, न कि बिल्ट-इन नेमस्पेस का, और इन्हें कभी भी पहचानकर्ता के रूप में उपयोग नहीं किया जा सकता।
ब्लॉक्स, लूप्स, और कॉम्प्रिहेंशंस
Python के ब्लॉक स्टेटमेंट्स और कॉम्प्रिहेंशंस के विशेष स्कोपिंग व्यवहार होते हैं, जो अक्सर अन्य भाषाओं से आने वाले डेवलपर्स को चकित करते हैं।
if/for/while/with के लिए ब्लॉक स्कोप नहीं होता
इन ब्लॉक्स के अंदर असाइनमेंट्स घेरने वाले स्कोप (फ़ंक्शन या मॉड्यूल) को प्रभावित करते हैं। लूप वाले वेरिएबल भी लूप के समाप्त होने के बाद परिभाषित रहते हैं।
if True:
status = "ready"
print(status) # "ready"
for i in range(3):
pass
print(i) # 2 (the last value from the loop)
कॉम्प्रिहेंशंस अपने iteration वेरिएबल को अलग रखते हैं
List, dict, और set कॉम्प्रिहेंशंस के लूप वेरिएबल्स के लिए अपना लोकल स्कोप होता है। iteration वेरिएबल बाहरी स्कोप में लीक नहीं होता।
numbers = [1, 2, 3]
[x for x in numbers]
print("x" in globals() or "x" in locals()) # False
Python 3.12 (PEP 709) ने गति के लिए कॉम्प्रिहेंशंस को इनलाइन किया, जबकि यह आइसोलेशन बरकरार रहा; आपको अभी भी स्पष्ट, न लीक होने वाले लूप वेरिएबल्स के साथ तेज़ निष्पादन मिलता है।
global कीवर्ड का उपयोग
जब आपको मॉड्यूल-स्तरीय वेरिएबल को फिर से बाँधना हो, तो फ़ंक्शन के अंदर किसी नाम को global घोषित करें। घोषणा को फ़ंक्शन के शीर्ष के पास रखें ताकि स्पष्ट रहे कि आप किस वेरिएबल को संशोधित कर रहे हैं।
tax_rate = 0.08
def configure_tax(rate):
global tax_rate
tax_rate = float(rate)
def total_with_tax(cents):
return int(cents * (1 + tax_rate))
configure_tax(0.10)
print(total_with_tax(1000)) # 1100
nonlocal कीवर्ड का उपयोग
निकटतम घेरने वाले फ़ंक्शन स्कोप से किसी नाम को फिर से बाँधने के लिए nonlocal का उपयोग करें। यह उन क्लोज़र्स में सामान्य है जो स्टेट बनाए रखते हैं।
def make_accumulator(start=0):
total = start
def add(amount):
nonlocal total
total += amount
return total
return add
acc = make_accumulator()
print(acc(5)) # 5
print(acc(10)) # 15
nonlocal का उपयोग किसी ऐसे नाम के लिए करना जो किसी घेरने वाले फ़ंक्शन में परिभाषित नहीं है, SyntaxError होता है। यदि नाम ग्लोबल है, तो global का उपयोग करें।
locals() और globals() 2025 में
ये फ़ंक्शंस निरीक्षण और डिबगिंग के लिए उपयोगी हैं, वेरिएबल अपडेट करने के लिए नहीं। Python 3.13 (PEP 667) से शुरू होकर, किसी फ़ंक्शन में locals() के प्रत्येक कॉल पर एक स्वतंत्र स्नैपशॉट लौटाया जाता है। उस स्नैपशॉट को संपादित करने से वास्तविक लोकल्स नहीं बदलते। मैंने यह व्यवहार Python 3.13 पर निम्न स्निपेट चलाकर पुष्टि किया:
def probe():
project = "alpha"
snap1 = locals()
snap1["project"] = "beta" # edits the snapshot only
observed = project # still "alpha"
snap2 = locals() # new snapshot
return snap1["project"], observed, snap2["project"]
print(probe()) # ('beta', 'alpha', 'alpha')
globals() का उपयोग भी मॉड्यूल नेमस्पेस के लिए इसी तरह करें। एप्लिकेशन कोड लिखते समय डायनामिक लुकअप्स की बजाय स्पष्ट पैरामीटर और रिटर्न वैल्यू को प्राथमिकता दें।
आम गलतियाँ और उनके समाधान
व्यवहार में मुझे दिखने वाली अधिकांश स्कोप-संबंधी त्रुटियों के पीछे ये गलतियाँ होती हैं।
UnboundLocalErrorफ़ंक्शन में असाइन करने से: यदि किसी नाम को फ़ंक्शन में कहीं भी असाइन किया जाता है, तो Python उसे लोकल मानता है। या तो रीड को असाइनमेंट के बाद ले जाएँ, नाम बदलें, या उपयुक्त रूप से global/nonlocal जोड़ें।
- ब्लॉक स्कोप की अपेक्षा करना: if/for/while/with में असाइन किए गए नाम घेरने वाले स्कोप में बने रहते हैं। स्कोप सीमित करने के लिए छोटे सहायक फ़ंक्शंस का उपयोग करें।
- बिल्ट-इन्स को शैडो करना: list, dict, sum, id, या min जैसे नामों से बचें। वर्णनात्मक नाम चुनें, जैसे
customer_listयाmin_allowed।
- क्लोज़र्स में
nonlocalभूल जाना: यदि आप बाहरी फ़ंक्शन के वेरिएबल को अपडेट करना चाहते हैं, तो उसे nonlocal घोषित करें। नहीं तो आप एक नया लोकल बना देंगे और बाहरी मान नहीं बदलेगा।
- कीवर्ड्स को बिल्ट-इन्स समझ लेना: कीवर्ड्स (सिंटैक्स) को कभी भी नाम के रूप में उपयोग नहीं किया जा सकता। बिल्ट-इन्स को शैडो किया जा सकता है, पर करना नहीं चाहिए।
ऐसी सर्वोत्तम प्रथाएँ जो स्कोप को सरल रखती हैं
ये आदतें कोड को पढ़ना, टेस्ट करना और डिबग करना आसान बनाती हैं।
- छोटे, केंद्रित फ़ंक्शंस को प्राथमिकता दें। वेरिएबल्स को सबसे संकरे स्कोप में परिभाषित करें जो काम करे।
- डेटा को पैरामीटर और रिटर्न वैल्यू के जरिए अंदर-बाहर पास करें। मॉड्यूल-स्तरीय स्टेट को न्यूनतम रखें।
- क्लोज़र्स का सोचे-समझे उपयोग करें। कौन-से बाहरी नाम नेस्टेड फ़ंक्शन कैप्चर करता है, इसे डॉक्युमेंट करें, और जब सच में रिबाइंड करना हो तभी nonlocal उपयोग करें।
- वर्णनात्मक, गैर-विरोधी नाम चुनें। अग्रणी अंडरस्कोर (जैसे
_cache) आंतरिक उपयोग का संकेत देता है।
- locals()/globals() को रीड-ओनली डायग्नोस्टिक्स समझें, न कि कॉन्फ़िगरेशन मैकेनिज़्म।
निष्कर्ष
Python नामों को Local → Enclosing → Global → Built-in स्कोप्स में खोजकर हल करता है। इस क्रम—और कब global या nonlocal का उपयोग करना है—को समझ लेने से NameError और UnboundLocalError जैसी आम त्रुटियाँ दूर हो जाती हैं। वेरिएबल्स को यथासंभव छोटे स्कोप में रखें, बिल्ट-इन्स को शैडो करने से बचें, और क्लोज़र्स का जानबूझकर उपयोग करें। इस मानसिक मॉडल के साथ, आपके फ़ंक्शंस पूर्वानुमेय ढंग से व्यवहार करते हैं, और स्कोप आश्चर्य का स्रोत नहीं रहता।
मैंने विभिन्न उद्योगों में काम किया है और कई भूमिकाएँ निभाई हैं: सॉफ़्टवेयर डेवलपर, मशीन लर्निंग शोधकर्ता, डाटा साइंटिस्ट, प्रोडक्ट मैनेजर। लेकिन मूल रूप से, मैं एक प्रोग्रामर हूँ जिसे सीखना और ज्ञान बाँटना पसंद है!
