मॉड्यूल्स और इम्पोर्ट
अपनी लिखी फ़ाइल को दूसरी फ़ाइल से इस्तेमाल करना, import करने पर वास्तव में क्या होता है, `if __name__ == "__main__"` क्यों ज़रूरी है, स्टैंडर्ड लाइब्रेरी, और नाम के टकराव का जाल।
आप कैसे सीखना चाहते हैं?
वह समस्या जिसे हम हल कर रहे हैं
पिछले अध्याय में हमने एक line_total फ़ंक्शन लिखा था। अब एक दूसरा प्रोग्राम लिखते समय वही फिर से चाहिए।
सबसे आसान तरीका — कॉपी करके चिपका देना। काम भी करता है, पहले दिन।
फिर टैक्स की दर बदलती है। आप एक फ़ाइल में ठीक करते हैं, दूसरी याद नहीं रहती। अब दो प्रोग्राम एक ही ऑर्डर के दो अलग टोटल दिखा रहे हैं, और कौन-सा सही है यह बताने का कोई तरीका नहीं — क्योंकि दोनों "सही" हैं, अपनी-अपनी फ़ाइल के हिसाब से।
समस्या कॉपी करने की मेहनत नहीं है। समस्या यह है कि सच अब दो जगह है।
import pricing
print(pricing.TAX_RATE)
print(pricing.line_total(15.0, 3))0.15
51.75एक फ़ाइल, और जितने प्रोग्राम चाहिए वे सब उसी को इस्तेमाल करते हैं। दर बदले तो एक ही जगह बदलती है।
यह अध्याय वही एक लाइन है — import — और उसके पीछे असल में क्या होता है।
इस अध्याय के अंत तक आप कर पाएंगे
- अपनी लिखी एक फ़ाइल को दूसरी फ़ाइल से इम्पोर्ट करना
import x,from x import yऔरimport x as zका फ़र्क़ बताना- बताना कि इम्पोर्ट के समय असल में क्या होता है, और कितनी बार होता है
- समझाना कि
if __name__ == "__main__":क्यों लिखा जाता है - स्टैंडर्ड लाइब्रेरी से मॉड्यूल लाना
- जानना कि पायथन कहाँ ढूँढता है, और फ़ाइल के नाम से किसी मॉड्यूल को ढक देने वाले जाल से बचना
पहले से क्या आना चाहिए: स्कोप और म्यूटेबल डिफ़ॉल्ट।
मॉड्यूल मतलब बस एक फ़ाइल
pricing.py नाम की फ़ाइल है, तो वह pricing नाम का एक मॉड्यूल है। अलग से कुछ घोषित नहीं करना पड़ता, कोई ख़ास लाइन नहीं लिखनी पड़ती — फ़ाइल का होना ही काफ़ी है।
import pricing लिखने पर पूरा मॉड्यूल आता है, और अंदर की चीज़ों तक डॉट (.) से पहुँचना होता है: pricing.TAX_RATE, pricing.line_total(...)।
डॉट बेकार की टाइपिंग लग सकता है, लेकिन वह एक काम करता है — पढ़ते समय बता देता है कि चीज़ कहाँ से आई है। बड़ी फ़ाइल में यह बहुत काम आता है।
सिर्फ़ वही लाना जो चाहिए
from pricing import line_total
print(line_total(15.0, 3))
print(TAX_RATE)51.75
NameError: name 'TAX_RATE' is not definedfrom ... import ... सिर्फ़ वही नाम लाता है जो माँगा गया, और उसे सीधे इस्तेमाल किया जा सकता है — डॉट की ज़रूरत नहीं।
(एरर से पहले पायथन traceback की कुछ लाइनें भी प्रिंट करता है; इस अध्याय में आम तौर पर सिर्फ़ आख़िरी लाइन दिखाई गई है।)
लेकिन बाकी नाम नहीं आते। TAX_RATE माँगा नहीं गया, इसलिए वह यहाँ नहीं है — भले ही उसी फ़ाइल में बैठा है।
कब कौन-सा? मोटा नियम: दो-तीन नाम चाहिए तो from, बहुत कुछ चाहिए तो पूरा मॉड्यूल। और एक चीज़ कभी न करें — from pricing import *। यह चुपचाप सारे नाम खींच लाता है, और फिर line_total कहाँ से आया यह पता करने का कोई तरीका नहीं बचता।
नाम बदल लेना
import pricing as p
print(p.line_total(15.0, 3))51.75as मॉड्यूल को इस फ़ाइल के अंदर एक छोटा नाम देता है।
इसे तभी लिखें जब नाम सच में लंबा हो, या आम चलन ही ऐसा हो। आलस में एक अक्षर का नाम देंगे तो पढ़ने की सुविधा ही ख़त्म हो जाती है — p.line_total(...) देखकर कोई नहीं बता सकता कि चीज़ कहाँ की है।
इम्पोर्ट करने का मतलब है फ़ाइल को चलाना
यह बात ज़्यादातर लोग बाद में जाकर खोजते हैं, अक्सर ग़लत समय पर।
tools.py:
print("tools is being set up")
def ping():
return "pong"main.py:
print("main starts")
import tools
import tools
print(tools.ping())
print("main ends")main starts
tools is being set up
pong
main endsदो चीज़ें ध्यान दें।
इम्पोर्ट करने का मतलब है फ़ाइल को ऊपर से नीचे तक चलाना। def लाइनें फ़ंक्शन बनाती हैं, और बाकी जो कुछ है — जैसे वह print — वह सच में होता है। मॉड्यूल के अंदर टॉप-लेवल पर print या input रखेंगे तो जो भी उसे इम्पोर्ट करेगा, उसके सिर आ पड़ेगा।
दूसरे import tools ने कुछ नहीं किया। एक मॉड्यूल प्रोग्राम में सिर्फ़ एक बार चलता है; उसके बाद पायथन पहले वाला ही लौटा देता है। इसलिए एक ही मॉड्यूल को दस जगह इम्पोर्ट करना सुरक्षित भी है और सस्ता भी।
__name__ और वह मशहूर लाइन
हर मॉड्यूल के अंदर __name__ नाम की एक चीज़ होती है, जिसे पायथन ख़ुद सेट करता है।
tools.py:
def ping():
return "pong"
print("module name is", __name__)
if __name__ == "__main__":
print("running directly")python tools.py चलाने पर:
module name is __main__
running directlyलेकिन किसी दूसरी फ़ाइल से import tools करने पर:
module name is toolsrunning directly वाली लाइन इस बार नहीं आई, क्योंकि शर्त ग़लत (false) निकली।
नियम सीधा है: जिस फ़ाइल को आप चला रहे हैं उसका __name__ "__main__" होता है; जो इम्पोर्ट हो रही हैं उनका __name__ उनका अपना नाम होता है।
इसलिए if __name__ == "__main__": के अंदर का हिस्सा सिर्फ़ सीधे चलाने पर चलता है, इम्पोर्ट करने पर नहीं।
इसकी ज़रूरत पिछले हिस्से से ही समझ आती है। इम्पोर्ट मतलब फ़ाइल चलाना — तो अगर फ़ाइल के आख़िर में main() लिखा रहेगा, तो जो भी उसे इम्पोर्ट करेगा उसका प्रोग्राम अचानक आपका प्रोग्राम भी चला देगा। यह लाइन वह दरवाज़ा बंद करती है।
स्टैंडर्ड लाइब्रेरी
पायथन के साथ ही बहुत सारे मॉड्यूल आते हैं। कुछ इंस्टॉल नहीं करना पड़ता।
import math
import statistics
print(math.sqrt(144))
print(math.floor(3.7), math.ceil(3.2))
print(statistics.mean([10, 20, 60]))12.0
3 4
30एक और, जो अक्सर काम आता है:
import random
random.seed(7)
print(random.randint(1, 100))
print(random.choice(["pen", "bag", "ink"]))42
penrandom.seed(7) न हो तो हर बार अलग नंबर आते — मक़सद भी तो यही है। seed सेट करने से क्रम तय हो जाता है, इसलिए उसी पायथन वर्ज़न पर ऊपर वाला आउटपुट आपकी मशीन पर भी वही आएगा। टेस्ट लिखते समय यह बहुत ज़रूरी है: किसी रैंडम चीज़ को टेस्ट करना हो तो पहले उसे दोहराने लायक बनाना पड़ता है।
यह "रैंडम" नहीं, "रैंडम जैसा" है।randomएक कैलकुलेशन से नंबर बनाता है, इसलिए एक ही seed से हमेशा एक ही क्रम आता है। पासवर्ड या टोकन के लिए इसे इस्तेमाल न करें — उसके लिएsecretsमॉड्यूल है।
पायथन कहाँ ढूँढता है
import pricing लिखने पर पायथन क्रम से ढूँढता है: पहले इंटरप्रेटर के अंदर बने मॉड्यूल, फिर जो स्क्रिप्ट चल रही है उसका अपना फ़ोल्डर, फिर स्टैंडर्ड लाइब्रेरी, फिर इंस्टॉल किए गए पैकेज।
इस क्रम का एक नतीजा है, और वह लोगों को परेशान करता है।
आपके फ़ोल्डर में random.py नाम की एक फ़ाइल है, और आपने लिखा:
import random
print(random.choice([1, 2, 3]))AttributeError: module 'random' has no attribute 'choice'आपकी फ़ाइल असली random से पहले मिली, इसलिए वही इम्पोर्ट हुई — और उसके अंदर choice नहीं है।
मैसेज उलझाने वाला है, क्योंकि वह कह रहा है कि random में choice नहीं है, जो सुनने में नामुमकिन लगता है। लेकिन वह आपके random की बात कर रहा है।
नियम: आपकी फ़ाइल का नाम किसी मॉड्यूल के नाम से मेल नहीं खाना चाहिए। random.py, string.py, json.py, csv.py — ये मासूम नाम लगते हैं, और ठीक इसी वजह से जाल काम करता है।
एक अपवाद, जो नियम को ही समझाता है। कई इंस्टॉलेशन में (जैसे वह लिनक्स जिस पर इस अध्याय के उदाहरण चलाए गए)math.pyनाम की फ़ाइल बनाकरimport mathकरें तो असलीmathही आता है, आपका नहीं। क्योंकि वहाँmathइंटरप्रेटर के अंदर बना है, और ऐसे मॉड्यूल ढूँढने के क्रम में सबसे पहले आते हैं। कौन-सा मॉड्यूल अंदर बना है, यह इस पर निर्भर करता है कि पायथन कैसे बनाया गया। यानी जाल कुछ नामों पर काम करता है, कुछ पर नहीं — जिसे याद रखने से आसान है नाम बदल देना।
फ़ोल्डर में सजाना
फ़ाइलें बढ़ने लगें तो उन्हें फ़ोल्डरों में बाँटा जा सकता है। फ़ोल्डर के अंदर एक __init__.py फ़ाइल रखें — ख़ाली हो तब भी चलेगा — तो फ़ोल्डर एक पैकेज बन जाता है।
shop/
__init__.py
pricing.py
main.pyफिर डॉट से रास्ता लिखा जा सकता है:
from shop.pricing import line_total
print(line_total(15.0, 3))51.75अभी के लिए इतना काफ़ी है। इस अध्याय के बाकी हिस्से में हम सीधी, एक फ़ोल्डर वाली फ़ाइलों पर ही रहेंगे।
एक पूरा उदाहरण
तीन फ़ाइलें, और उनके बीच निर्भरता एक ही दिशा में।
pricing.py:
"""Prices and tax. Nothing here prints, and nothing here asks for input."""
TAX_RATE = 0.15
def line_total(price, quantity):
"""One line of an order, tax included."""
return round(price * quantity * (1 + TAX_RATE), 2)
def order_total(lines):
"""`lines` is a list of (name, price, quantity) tuples."""
return round(sum(line_total(price, qty) for _, price, qty in lines), 2)report.py:
"""Turning numbers into lines of text. It imports pricing; pricing imports nothing."""
import pricing
def receipt(lines):
rows = [f"{name:<8} {pricing.line_total(price, qty):>8.2f}"
for name, price, qty in lines]
rows.append("-" * 17)
rows.append(f"{'total':<8} {pricing.order_total(lines):>8.2f}")
return rows
if __name__ == "__main__":
print("report.py has no data of its own to show")main.py:
"""The only file that is meant to be run."""
import statistics
from report import receipt
ORDER = [
("pen", 15.0, 3),
("bag", 850.0, 1),
("ink", 120.0, 2),
]
def main():
for row in receipt(ORDER):
print(row)
print()
quantities = [qty for _, _, qty in ORDER]
print("mean quantity:", statistics.mean(quantities))
if __name__ == "__main__":
main()python main.py चलाने पर:
pen 51.75
bag 977.50
ink 276.00
-----------------
total 1305.25
mean quantity: 2चार चीज़ें देखने लायक हैं।
निर्भरता एक दिशा में जाती है। main report को जानता है, report pricing को जानता है, और pricing किसी को नहीं जानता। यह क्रम एक ज़ंजीर बनाता है, और ज़ंजीर के आख़िरी सिरे वाली फ़ाइल — pricing — अकेले टेस्ट की जा सकती है। अगर pricing को भी report इम्पोर्ट करना पड़ता, तो दोनों में से कोई भी अकेला खड़ा नहीं हो पाता।
pricing.py कुछ प्रिंट नहीं करता, कुछ पूछता भी नहीं। वह सिर्फ़ वैल्यू लेता है और वैल्यू लौटाता है — अध्याय 21 के वही pure फ़ंक्शन। इम्पोर्ट मतलब फ़ाइल चलाना, इसलिए यह संयम शिष्टाचार नहीं, ज़रूरत है।
report.py में भी if __name__ == "__main__": है, और वह कोई काम की चीज़ नहीं करता — इस फ़ाइल का अपना कोई डेटा नहीं, तो दिखाने को भी कुछ नहीं। इसे हटाया जा सकता था। रखा गया है क्योंकि अगर फ़ाइल कभी सीधे चलाई जाए, तो कम से कम वह कुछ बोलती है — चुपचाप कुछ किए बिना रुक जाने से बेहतर।
main.py के अलावा कोई फ़ाइल अपने-आप कुछ नहीं करती। पूरे प्रोग्राम का सिर्फ़ एक ही दरवाज़ा है, और उसे पढ़ते ही समझ आता है कि क्या होगा।
जब कुछ टूट जाए
ModuleNotFoundError: No module named 'priceing' स्पेलिंग ग़लत है, या फ़ाइल किसी दूसरे फ़ोल्डर में है। देखें कि जो स्क्रिप्ट चला रहे हैं उसके बगल में ही फ़ाइल है या नहीं।
ImportError: cannot import name 'line_totl' from 'pricing' मॉड्यूल मिल गया, अंदर का नाम नहीं मिला। मैसेज के आख़िर में ब्रैकेट में फ़ाइल का पूरा पाथ होता है, और अक्सर पास वाला नाम भी बता देता है — Did you mean: 'line_total'?
AttributeError: module 'random' has no attribute 'choice' लगभग हमेशा आपकी अपनी कोई फ़ाइल असली मॉड्यूल को ढक रही है। फ़ोल्डर में उस नाम की फ़ाइल ढूँढें, और उसका नाम बदल दें। इतना काफ़ी है — .py फ़ाइल न रहे तो __pycache__ में पड़ी पुरानी कॉपी पायथन इस्तेमाल नहीं करता।
ImportError: cannot import name 'rows' from partially initialized module 'table' (most likely due to a circular import) दो फ़ाइलें एक-दूसरे को इम्पोर्ट कर रही हैं, और कम से कम एक from ... import ... से ऐसा नाम माँग रही है जो दूसरी फ़ाइल में अभी बना ही नहीं — क्योंकि वह फ़ाइल ख़ुद अभी ऊपर से नीचे तक चलना पूरा नहीं कर पाई। आम उपाय है कि जो हिस्सा दोनों को चाहिए उसे एक तीसरी फ़ाइल में ले जाएँ, जो किसी को इम्पोर्ट नहीं करती।
इम्पोर्ट करते ही कुछ प्रिंट हो गया या इनपुट माँगने लगा मॉड्यूल के टॉप-लेवल पर कोड है। उसे एक फ़ंक्शन में डालें, और if __name__ == "__main__": के अंदर से कॉल करें।
फ़ाइल एडिट की, लेकिन पुराना व्यवहार ही दिख रहा है प्रोग्राम फिर से चलाएँ — चलता हुआ प्रोग्राम मॉड्यूल को दोबारा नहीं पढ़ता। नोटबुक इस्तेमाल कर रहे हैं तो कर्नेल रीस्टार्ट करें।
चरण 4 / 6 — अनुमान
अपनी समझ की जाँच करें
line_total माँगा गया है, TAX_RATE नहीं। क्या होगा?
# pricing.py
TAX_RATE = 0.15
def line_total(price, quantity):
return round(price * quantity * (1 + TAX_RATE), 2)
# main.py
from pricing import line_total
print(line_total(15.0, 3))
print(TAX_RATE)- A`51.75` प्रिंट होता है, फिर `None`
- B`51.75` और `0.15` — दोनों प्रिंट होते हैं
- C`51.75` प्रिंट होता है, फिर `NameError`
- D`ImportError`
import tools दो बार लिखा गया है। क्या प्रिंट होगा?
# tools.py
print("tools is being set up")
def ping():
return "pong"
# main.py
print("main starts")
import tools
import tools
print(tools.ping())
print("main ends")- Atools is being set up main starts pong main ends
- Bmain starts pong main ends
- Cmain starts tools is being set up pong main ends
- Dmain starts tools is being set up tools is being set up pong main ends
python main.py चलाया जा रहा है। क्या प्रिंट होगा?
# tools.py
print("name:", __name__)
if __name__ == "__main__":
print("direct")
# main.py
import tools
print("done")- Aname: tools done
- Bname: main done
- Cname: __main__ direct done
- Dname: tools direct done
उत्तर देने के लिए अकाउंट आवश्यक है
अपने उत्तर जाँचने के लिए साइन इन करें
प्रश्न ऊपर दिए गए हैं, और मन में उत्तर सोचना ही मुख्य कार्य है। सही उत्तर, व्याख्या और तीन-स्तरीय संकेत देखने के लिए साइन इन करें।
आपकी बारी
एक फ़ोल्डर में तीन फ़ाइलें बनाएँ।
textutil.py — दो फ़ंक्शन: initials(name) जो "rafi ahmed" से "RA" बनाता है, और shorten(text, limit) जो लंबी स्ट्रिंग को काटकर आख़िर में ... लगाता है। इस फ़ाइल में कोई print नहीं होगा।
table.py — textutil इम्पोर्ट करता है, और rows(people) नाम का एक फ़ंक्शन देता है जो नामों की लिस्ट से सजी हुई लाइनों की लिस्ट लौटाता है।
main.py — table इम्पोर्ट करता है, कुछ नामों के साथ कॉल करता है, और प्रिंट करता है। तीनों फ़ाइलों में if __name__ == "__main__": रखें।
फिर चार प्रयोग, हर एक अपनी आँखों से देखने लायक:
textutil.pyमें सबसे ऊपरprint("loaded")लिखें, फिरpython main.pyचलाएँ। क्या लाइन दिखती है? कब —main.pyके पहलेprintसे पहले या बाद में?main.pyमेंimport tableदो बार लिखें।loadedकितनी बार प्रिंट होता है?- फ़ोल्डर में
statistics.pyनाम की एक ख़ाली फ़ाइल बनाएँ, फिरmain.pyमेंimport statisticsकरकेstatistics.mean([1, 2])कॉल करें। मैसेज पढ़ें — वह किसstatisticsकी बात कर रहा है? textutil.pyमें सबसे ऊपरfrom table import rowsजोड़ें, ताकि दोनों एक-दूसरे को इम्पोर्ट करें, फिरpython main.pyचलाएँ। मैसेज की आख़िरी लाइन ऐसे शुरू होगी:ImportError: cannot import name 'rows' from partially initialized module 'table'। (सिर्फ़import tableलिखने पर कोई एरर नहीं आता: तबtextutilको बस आधे-बने मॉड्यूल का एक नाम मिलता है, औरrowsबाद में, फ़ंक्शन चलते समय ढूँढा जाता है।from table import rowsनाम अभी माँगता है, जबकिtable.pyतब भी अपनी पहली लाइन पर ही अटका है।)
आख़िरी दो प्रयोग ऐसी दो ग़लतियाँ हैं जो मैसेज पढ़कर पहचान न हों तो घंटों खा सकती हैं। एक बार ख़ुद करके देख लें, तो अगली बार कुछ सेकंड।
चरण 6 / 6
चुनौती — अध्याय की क्विज़
सरल से कठिन — दस प्रश्न, अंतिम वाले जानबूझकर चुनौतीपूर्ण बनाए गए हैं।
क्विज़ देने के लिए साइन इन करें