अध्याय 18

कॉम्प्रिहेंशन — एक पंक्ति में लूप

लिस्ट, डिक्शनरी और सेट कॉम्प्रिहेंशन, फ़िल्टर करने वाले if और चुनने वाले if/else का अंतर, और कब कॉम्प्रिहेंशन लिखना गलत चुनाव है।

30 मिनटPython 3.12
  1. 1समस्या
  2. 2समझें
  3. 3उदाहरण
  4. 4अनुमान
  5. 5स्वयं करें
  6. 6चुनौती

वह समस्या जिसे हम हल कर रहे हैं

कोड का एक पैटर्न पिछले छह अध्यायों में बार-बार सामने आया है:

python
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)
text
[144, 90, 180, 66]
[144, 90, 180, 66]

ऊपर की चार पंक्तियाँ और नीचे की एक पंक्ति बिल्कुल वही काम करती हैं।

उन चार में से तीन पंक्तियाँ वास्तविक काम नहीं हैं, वे केवल व्यवस्था संबंधी हैं — एक खाली सूची बनाएं, स्रोत पर लूप चलाएं, हर बार append करें। एकमात्र वास्तविक निर्णय mark * 2 है। बाकी सब कुछ हर बार एक जैसा होता है।

एक कॉम्प्रिहेंशन (Comprehension) उस दोहराव को हटा देता है। यह कोई नई क्षमता नहीं है — यह वही है जो आप पहले से लिख सकते थे, कम शब्दों में।

इस मिशन के अंत तक आप सीखेंगे

  • लिस्ट कॉम्प्रिहेंशन लिखना और पढ़ना
  • if के साथ फ़िल्टर करना, और if/else के साथ चुनना
  • डिक्शनरी और सेट कॉम्प्रिहेंशन लिखना
  • यह बताना कि कब कॉम्प्रिहेंशन का उपयोग करना गलत विकल्प है
  • एक अपठनीय कॉम्प्रिहेंशन को वापस सामान्य लूप में बदलना

पूर्वापेक्षाएँ: नेस्टेड डेटा — डिक्शनरी की लिस्ट.


संरचना

text
[ मुझे क्या चाहिए   for   प्रत्येक वस्तु   in   यह कहाँ से आता है ]

इसे ज़ोर से पढ़ने पर यह एक साधारण वाक्य है: "`marks` में प्रत्येक `mark` के लिए `mark 2`"*.

इसे समझने का एक तरीका: पहले for भाग को पढ़ें, फिर शुरुआत को।

फ़िल्टर करना (Filtering)

अंत में एक if केवल उन चीज़ों को रखता है जो शर्त से मेल खाती हैं:

python
marks = [72, 45, 90, 33]

passed = [mark for mark in marks if mark >= 40]
print(passed)
text
[72, 45, 90]

फ़िल्टरिंग और ट्रांसफ़ॉर्मिंग एक साथ किए जा सकते हैं:

python
names = ["  rafi ", "AHMED", " dia"]

clean = [name.strip().lower() for name in names if name.strip()]
print(clean)
text
['rafi', 'ahmed', 'dia']

शर्त name.strip() बिना किसी तुलना के है। याद रखें: खाली टेक्स्ट falsy होता है। इसलिए केवल रिक्त स्थान (spaces) टाइप करने वाले को हटा दिया जाता है।

अंत में if बनाम शुरुआत में if

दो अलग-अलग चीज़ें हैं, और स्थिति ही पूरा अंतर है:

python
marks = [72, 33, 90]

labels = ["pass" if m >= 40 else "fail" for m in marks]
print(labels)
text
['pass', 'fail', 'pass']

अंत में if फ़िल्टर करता है — कुछ चीज़ें परिणाम तक कभी नहीं पहुँचतीं, इसलिए परिणाम छोटा हो सकता है।

शुरुआत में if ... else चुनता है — प्रत्येक वस्तु कुछ न कुछ उत्पन्न करती है, इसलिए लंबाई अपरिवर्तित रहती है।

क्या परिणाम की लंबाई बदलती है? यह सवाल दोनों को अलग करने का सबसे आसान तरीका है।

डिक्शनरी और सेट भी

वही संरचना, अलग कोष्ठक:

python
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)
text
{'rafi': 72, 'bilal': 90}
{'rafi': 144, 'ahmed': 90, 'bilal': 180}

मँझले कोष्ठक और एक कोलन एक डिक्शनरी कॉम्प्रिहेंशन बनाते हैं।

बिना कोलन के वही मँझले कोष्ठक एक सेट बनाते हैं:

python
orders = [
    {"item": "pen"},
    {"item": "bag"},
    {"item": "pen"},
]

items = {order["item"] for order in orders}
print(sorted(items))
text
['bag', 'pen']

अध्याय 16 का "कितने अलग-अलग" प्रश्न एक ही पंक्ति में। pen दो बार आता है और एक बार दिखाई देता है।

संख्याओं को टेक्स्ट में बदलना

यह पंक्ति अध्याय 13 में आई थी:

python
marks = [72, 45, 90]
print(", ".join(str(m) for m in marks))
print([str(m) for m in marks])
text
72, 45, 90
['72', '45', '90']

str(m) for m in marks बिना चौकोर कोष्ठक के एक कॉम्प्रिहेंशन है — इस रूप को जनरेटर एक्सप्रेशन (generator expression) कहा जाता है।

इसे कब नहीं लिखना चाहिए

कॉम्प्रिहेंशन लिखने में सक्षम होने का मतलब यह नहीं है कि आपको हमेशा लिखना ही चाहिए। इसे देखें:

python
rows = [["1", "2"], ["3", "x"]]

numbers = [int(c) for row in rows for c in row if c.isdigit()]
print(numbers)
text
[1, 2, 3]

पंक्ति सही है। लेकिन इसमें दो for और एक if हैं। छह महीने बाद उस पंक्ति को पढ़ना मुश्किल होगा।

लूप के रूप में:

python
rows = [["1", "2"], ["3", "x"]]

numbers = []
for row in rows:
    for c in row:
        if c.isdigit():
            numbers.append(int(c))

print(numbers)
text
[1, 2, 3]

अधिक पंक्तियाँ, लेकिन स्पष्ट सोच। कोड का लक्ष्य छोटा होना नहीं बल्कि स्पष्ट होना है।

एक व्यावहारिक नियम: एक for और अधिकतम एक if — कॉम्प्रिहेंशन। उससे अधिक — एक लूप। और कभी भी कॉम्प्रिहेंशन के अंदर print या append न रखें।


एक संपूर्ण उदाहरण

views.py:

python
# रिकॉर्ड्स की एक सूची, तीन अलग-अलग दृश्य (views)
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))
text
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.0

जब यह टूटता है

अंत में if और else रखने से SyntaxError [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 का उपयोग नहीं कर सकते।