कॉम्प्रिहेंशन — एक लाइन में लूप
लिस्ट, डिक्शनरी और सेट कॉम्प्रिहेंशन, फ़िल्टर करने वाले if और चुनने वाले if/else का अंतर, और कब कॉम्प्रिहेंशन लिखना गलत चुनाव है।
आप कैसे सीखना चाहते हैं?
वह समस्या जिसे हम हल कर रहे हैं
पिछले छह अध्यायों में लिखने का एक तरीका बार-बार लौटकर आया है:
marks = [72, 45, 90, 33]
doubled = []
for mark in marks:
doubled.append(mark * 2)
print(doubled)
doubled = [mark * 2 for mark in marks]
print(doubled)[144, 90, 180, 66]
[144, 90, 180, 66]ऊपर की चार लाइनें और नीचे की एक लाइन हूबहू एक ही काम करती हैं।
चार लाइनों में से तीन असल में काम की बात नहीं, इंतज़ाम की बात हैं — एक खाली लिस्ट बनाना, उस पर लूप चलाना, हर एक पर append करना। असली फ़ैसला सिर्फ़ एक है: mark * 2। बाकी हर बार एक जैसा।
कॉम्प्रिहेंशन वह दोहराव हटा देता है। यह कोई नई ताकत नहीं है — जो आप पहले से लिख सकते थे वही, कम शब्दों में। और इसी वजह से इसकी एक सीमा भी है, जो इस अध्याय के आखिरी हिस्से का विषय है: जिसे छोटा करने से पढ़ना आसान हो उसे ही छोटा करना चाहिए, हर चीज़ को नहीं।
इस अध्याय के अंत तक आप कर पाएंगे
- लिस्ट कॉम्प्रिहेंशन लिखना और पढ़ना
ifसे छाँटना, औरif/elseसे चुनना- डिक्शनरी और सेट कॉम्प्रिहेंशन लिखना
- बताना कि कब कॉम्प्रिहेंशन नहीं लिखना चाहिए
- एक न पढ़े जा सकने वाले कॉम्प्रिहेंशन को फिर से लूप में तोड़ना
पहले से क्या आना चाहिए: नेस्टेड डेटा — डिक्शनरी की लिस्ट।
ढाँचा
[ result for item in source ]यहाँ result वह है जो आप बनाना चाहते हैं, item हर बार आने वाली एक चीज़ है, और source वह लिस्ट (या कोई और कलेक्शन) है जहाँ से चीज़ें आ रही हैं।
अंग्रेज़ी में पढ़ें तो वाक्य सीधा है: "`mark 2 for each mark in marks`"*।
याद रखने का एक तरीका: for वाला हिस्सा पहले पढ़ें, फिर शुरुआत वाला। चीज़ें कहाँ से आ रही हैं यह पहले पता हो, तो क्या बन रहा है यह समझना आसान होता है।
छाँटना
आखिर में एक if जोड़ें तो सिर्फ़ वही चीज़ें आती हैं जो शर्त पूरी करती हैं:
marks = [72, 45, 90, 33]
passed = [mark for mark in marks if mark >= 40]
print(passed)[72, 45, 90]यहाँ पहला हिस्सा सिर्फ़ mark है — कोई हिसाब नहीं, जैसा है वैसा ही। छाँटना और बदलना, दोनों एक साथ किए जा सकते हैं:
names = [" rafi ", "AHMED", " dia"]
clean = [name.strip().lower() for name in names if name.strip()]
print(clean)['rafi', 'ahmed', 'dia']शर्त है name.strip() — कोई तुलना नहीं। 8 नंबर अध्याय का truthiness वाला नियम याद करें: खाली स्ट्रिंग false होती है। इसलिए अगर किसी ने सिर्फ़ स्पेस लिखे हों, तो वह बाहर हो जाता है।
if आखिर में बनाम if शुरुआत में
ये दो अलग चीज़ें हैं, और जगह ही फ़र्क है:
marks = [72, 33, 90]
labels = ["pass" if m >= 40 else "fail" for m in marks]
print(labels)['pass', 'fail', 'pass']आखिर वाला if छाँटता है — कुछ चीज़ें नतीजे में नहीं आतीं, इसलिए नतीजे की लंबाई कम हो सकती है।
शुरुआत वाला if ... else चुनता है — हर चीज़ के लिए कुछ न कुछ ज़रूर आता है, इसलिए लंबाई वही रहती है। यह 15 नंबर अध्याय में देखी गई वही एक-लाइन वाली शर्त है, बस कॉम्प्रिहेंशन के अंदर।
लंबाई बदलती है या नहीं — यही सवाल दोनों को अलग पहचानने का सबसे आसान तरीका है।
डिक्शनरी और सेट भी
वही ढाँचा, बस ब्रैकेट अलग:
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
passed = {name: mark for name, mark in marks.items() if mark >= 50}
print(passed)
doubled = {name: mark * 2 for name, mark in marks.items()}
print(doubled){'rafi': 72, 'bilal': 90}
{'rafi': 144, 'ahmed': 90, 'bilal': 180}कर्ली ब्रैकेट और एक कोलन — डिक्शनरी कॉम्प्रिहेंशन। marks.items() हर बार एक टपल देता है, और name, mark उसे अनपैक करता है, ठीक वैसे ही जैसे लूप में करता।
कोलन के बिना वही कर्ली ब्रैकेट दें तो सेट:
orders = [
{"item": "pen"},
{"item": "bag"},
{"item": "pen"},
]
items = {order["item"] for order in orders}
print(sorted(items))['bag', 'pen']16 नंबर अध्याय का “कितने अलग” वाला सवाल एक लाइन में। pen दो बार आया, फिर भी सेट में एक ही बार है।
नंबर को स्ट्रिंग में — जो आप पहले देख चुके हैं
13 नंबर अध्याय में यह लाइन आई थी, और तब कहा गया था कि इसकी व्याख्या बाद में आएगी:
marks = [72, 45, 90]
print(", ".join(str(m) for m in marks))
print([str(m) for m in marks])72, 45, 90
['72', '45', '90']अब इसे पूरा पढ़ा जा सकता है। str(m) for m in marks एक कॉम्प्रिहेंशन ही है, बस स्क्वेयर ब्रैकेट के बिना — इस रूप को generator expression कहते हैं, और यह पूरी लिस्ट एक साथ नहीं बनाता, एक-एक करके join को देता है। जब यह किसी फ़ंक्शन का अकेला आर्ग्युमेंट हो तो स्क्वेयर ब्रैकेट हटाए जा सकते हैं, और बड़े डेटा में इससे मेमोरी बचती है।
कब न लिखें
कॉम्प्रिहेंशन लिखा जा सकता है, इसका मतलब यह नहीं कि लिखना ही चाहिए। यह देखिए:
rows = [["1", "2"], ["3", "x"]]
numbers = [int(c) for row in rows for c in row if c.isdigit()]
print(numbers)[1, 2, 3]लाइन सही काम करती है। लेकिन इसमें दो for और एक if है, और क्रम याद रखना पड़ता है: for को बाहर से अंदर की ओर पढ़ना होता है, जैसे नेस्टेड लूप में लिखा जाता। छह महीने बाद यह लाइन पढ़कर समझना मुश्किल होगा।
लूप में लिखें तो:
rows = [["1", "2"], ["3", "x"]]
numbers = []
for row in rows:
for c in row:
if c.isdigit():
numbers.append(int(c))
print(numbers)[1, 2, 3]ज़्यादा लाइनें, कम सोचना। कोड का मकसद छोटा होना नहीं, साफ़ होना है — और ज़्यादातर मामलों में छोटा करने से ही कोड साफ़ होता है, लेकिन हमेशा नहीं।
काम का नियम: एक for, और ज़्यादा से ज़्यादा एक if — कॉम्प्रिहेंशन। उससे ज़्यादा हो तो लूप। और कॉम्प्रिहेंशन के अंदर कभी print या append न लिखें; वह वैल्यू बनाने की चीज़ है, काम करवाने की नहीं।
एक पूरा उदाहरण
views.py:
# One list of records in, three different views out
orders = [
{"customer": "rafi", "item": "pen", "quantity": 3, "price": 15.0},
{"customer": "ahmed", "item": "bag", "quantity": 1, "price": 850.0},
{"customer": "rafi", "item": "ink", "quantity": 2, "price": 120.0},
{"customer": "dia", "item": "pen", "quantity": 5, "price": 15.0},
]
totals = [order["quantity"] * order["price"] for order in orders]
print("Totals :", totals)
big = [order["item"] for order in orders if order["quantity"] * order["price"] > 100]
print("Over 100 :", big)
items = {order["item"] for order in orders}
print("Distinct :", sorted(items))
by_item = {order["item"]: order["price"] for order in orders}
print("Price list:", by_item)
print("Grand :", sum(totals))Totals : [45.0, 850.0, 240.0, 75.0]
Over 100 : ['bag', 'ink']
Distinct : ['bag', 'ink', 'pen']
Price list: {'pen': 15.0, 'bag': 850.0, 'ink': 120.0}
Grand : 1210.017 नंबर अध्याय वाला वही डेटा, और वहाँ जो लूप में लिखा गया था उसका ज़्यादातर हिस्सा यहाँ एक-एक लाइन में।
तीन बातें ध्यान देने लायक हैं।
totals और sum(totals) अलग लाइनों में। कुल निकालने के लिए sum(order["quantity"] * order["price"] for order in orders) लिखा जा सकता था, लेकिन तब हर लाइन की वैल्यू हाथ में नहीं रहती। बीच के कदम को एक नाम देना अक्सर एक-लाइन वाली तरकीब से ज़्यादा काम का होता है।
big के अंदर एक ही हिसाब दो बार। शर्त में भी quantity * price, और नतीजे में भी चाहिए होता। यहाँ नतीजे में सिर्फ़ नाम लिया गया इसलिए समस्या नहीं हुई — लेकिन दोनों चाहिए हों तो यह कॉम्प्रिहेंशन छोड़कर लूप पर लौटने की अच्छी वजह है, क्योंकि लूप में वैल्यू एक बार निकालकर एक नाम में रखी जा सकती है।
by_item ने एक रिकॉर्ड खो दिया। चार ऑर्डर, लेकिन नतीजे में तीन जोड़ियाँ — pen दो बार है, और 15 नंबर अध्याय के नियम के मुताबिक बाद वाले ने पहले वाले को चुपचाप बदल दिया। यहाँ दोनों pen की कीमत एक है इसलिए नुकसान नहीं हुआ; अलग होती तो चुपचाप गलत नतीजा आता। कॉम्प्रिहेंशन यह समस्या पैदा नहीं करता, लेकिन छोटा होने की वजह से इसके नज़र में आने का मौका भी कम कर देता है।
जब कुछ टूट जाए
SyntaxError — if और else दोनों आखिर में लिखने पर [m for m in marks if m >= 40 else 0] नहीं चलता। छाँटने वाला if आखिर में बैठता है और उसका कोई else नहीं होता; चुनने वाला if ... else शुरुआत में बैठता है: [m if m >= 40 else 0 for m in marks]।
नतीजा None से भरा है कॉम्प्रिहेंशन के अंदर कुछ ऐसा चलाया गया है जो कुछ नहीं लौटाता — जैसे [items.append(x) for x in other]। append None लौटाता है, इसलिए नतीजा None की एक लिस्ट है। कॉम्प्रिहेंशन वैल्यू बनाने के लिए है; काम करवाना हो तो सामान्य लूप लिखें।
NameError — कॉम्प्रिहेंशन का वेरिएबल बाहर नहीं है [m * 2 for m in marks] लिखने के बाद बाहर m इस्तेमाल नहीं कर सकते। 11 नंबर अध्याय में for लूप का वेरिएबल लूप के बाद भी बचा रहता था; कॉम्प्रिहेंशन का वेरिएबल नहीं बचता — वह सिर्फ़ अंदर ही रहता है।
डिक्शनरी कॉम्प्रिहेंशन में जोड़ियाँ कम हैं एक ही key कई बार बनी, और आखिरी वाली ने पहले वालों को बदल दिया। len() से मिलाकर देखें।
लाइन पढ़ी नहीं जा रही यही काफ़ी वजह है। लूप में तोड़ दें — रफ़्तार का जो थोड़ा फ़र्क होता है वह सीखने के दौर में शायद ही मायने रखता है, और अगला इंसान (शायद आप ही) शुक्रगुज़ार रहेगा।
चरण 4 / 6 — अनुमान
अपनी समझ की जाँच करें
आखिर में एक if है। क्या प्रिंट होगा?
marks = [72, 45, 90, 33]
passed = [mark for mark in marks if mark >= 40]
print(passed)- A[72, 45, 90, 33]
- B[True, True, True, False]
- C[33]
- D[72, 45, 90]
इस बार if शुरुआत में है, साथ में else। क्या प्रिंट होगा?
marks = [72, 33, 90]
labels = ["pass" if m >= 40 else "fail" for m in marks]
print(labels)- A['pass', 'fail', 'pass']
- B['pass', 'pass']
- C[72, 90]
- D['pass', 'fail']
कॉम्प्रिहेंशन के अंदर append चलाया गया है। दोनों लाइनें क्या प्रिंट करेंगी?
source = ["pen", "bag"]
target = []
result = [target.append(item) for item in source]
print(target)
print(result)- A['pen', 'bag'] [None, None]
- Bएक `TypeError`
- C['pen', 'bag'] ['pen', 'bag']
- D[] ['pen', 'bag']
उत्तर देने के लिए अकाउंट आवश्यक है
अपने उत्तर जाँचने के लिए साइन इन करें
प्रश्न ऊपर दिए गए हैं, और मन में उत्तर सोचना ही मुख्य कार्य है। सही उत्तर, व्याख्या और तीन-स्तरीय संकेत देखने के लिए साइन इन करें।
आपकी बारी
clean.py नाम की एक फ़ाइल लिखें, जिसमें एक लिस्ट में कुछ बेतरतीब नाम हों — कुछ में फ़ालतू स्पेस, कुछ बड़े अक्षरों में, और कम से कम एक बिल्कुल खाली।
कॉम्प्रिहेंशन से बनाएँ:
- साफ़ नामों की एक लिस्ट — स्पेस हटे हुए, छोटे अक्षरों में, और खाली वाले बाहर
- हर नाम की लंबाई की एक लिस्ट
- पाँच अक्षर से लंबे नामों की एक लिस्ट
{name: length}के आकार की एक डिक्शनरी- नामों के पहले अक्षरों का एक सेट
फिर दो प्रयोग:
- नंबर 1 को एक सामान्य
forलूप से भी लिखें, और==से जाँचें कि दोनों के नतीजे बराबर हैं या नहीं - एक कॉम्प्रिहेंशन लिखें जिसमें दो
forऔर एकifहो, फिर उसे लूप में तोड़ दें। कौन-सा पढ़ने में आसान लग रहा है?
आखिरी सवाल का कोई “सही” जवाब नहीं है, और यही मुख्य बात है। यह सवाल खुद से पूछना सीखना ही इस अध्याय की असली सीख है।
चरण 6 / 6
चुनौती — अध्याय की क्विज़
सरल से कठिन — दस प्रश्न, अंतिम वाले जानबूझकर चुनौतीपूर्ण बनाए गए हैं।
क्विज़ देने के लिए साइन इन करें