2025, Sep 28 23:31

Python में लूप के भीतर डायनेमिक एट्रिब्यूट पढ़ने का सही तरीका

Python में लूप के अंदर डायनेमिक एट्रिब्यूट सही तरह से पढ़ना सीखें: getattr, setattr और instance.__dict__ के व्यावहारिक उदाहरण, सामान्य गलतियों से बचने के टिप्स.

Python में डायनेमिक एट्रिब्यूट एक्सेस तब तक सीधा लगता है जब तक आप उन्हें एक लूप के अंदर पढ़ने की कोशिश नहीं करते. लूप में obj.i लिखने से a, b या c के मान नहीं मिलेंगे—यही अड़चन है. उद्देश्य यह है कि आप इटरेट करें, डेटा के आधार पर एट्रिब्यूट सेट करें, और उसी क्रम में उन्हें वापस पढ़ सकें.

समस्या की रूपरेखा

इस छोटे से पैटर्न को देखें जिसमें रनटाइम पर एट्रिब्यूट जोड़े जाते हैं और फिर पढ़े जाते हैं:

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
  print(instance.tag)

मकसद यह है कि instance.a, instance.b और instance.c के मान दिखें—हर एक एक-तत्व वाली सूची हो. लेकिन आख़िरी लाइन instance.tag को ज्यों का त्यों पढ़ने की कोशिश करती है, जबकि सेट ही instance.tag नहीं किया गया था.

असल में हो क्या रहा है

लूप ठीक तरह से ऐसे एट्रिब्यूट बनाता है जिनके नाम लूप के मानों से निकले हैं. लेकिन instance.tag तक पहुँचना tag नाम के वैरिएबल का संदर्भ नहीं है; वह ठीक-ठीक "tag" नाम के एट्रिब्यूट को ढूँढता है. इसलिए "a", "b" और "c" के लिए अभी-अभी बनाए गए एट्रिब्यूट पढ़ने की उम्मीद में यह चूक जाता है. गणना किए गए नाम से मान लेने के लिए नाम-आधारित लुकअप चाहिए.

समाधान

एक साधारण Python ऑब्जेक्ट के एट्रिब्यूट instance.__dict__ के जरिए डिक्शनरी के रूप में मिलते हैं. इस मैपिंग को लूप में निकाले गए नाम से इंडेक्स करने पर वही मान मिल जाता है जिसे आपने अभी सेट किया था. इससे प्रिंट करना और जाँच के लिए सूची बनाना—दोनों काम हो जाते हैं.

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
  print(instance.__dict__[attr_id])

अगर आप setattr जैसी ही बिल्ट-इन कॉल के जरिए मान पाना चाहते हैं, तो उसी निकाले गए नाम के साथ getattr का उपयोग करें:

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
  print(getattr(instance, attr_id))

जाँच के लिए मानों को एक सूची में इकट्ठा करना हो तो उन्हीं कुंजियों पर इटरेट करें और नाम से पढ़ें:

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
values_snapshot = [instance.__dict__[name] for name in labels]
print(values_snapshot)

यह क्यों मायने रखता है

जब आप एट्रिब्यूट को डायनेमिक रूप से बनाते हैं, तो स्थिर पहचानकर्ता के साथ सीधा डॉट एक्सेस लूप में निकले वैरिएबल को प्रतिबिंबित नहीं करता. नाम-आधारित एक्सेस कोड को सुसंगत रखता है: नाम से सेट करें, नाम से पढ़ें. कई डिज़ाइनों में, क्लास के भीतर डिक्ट-समर्थित प्रॉपर्टी बैग इस पैटर्न को दर्शाने का सबसे साफ तरीका होता है, और कुछ स्थितियों में एट्रिब्यूट की जगह सीधे एक साधारण डिक्ट इस्तेमाल करना और भी सरल पड़ता है.

सार

जब एट्रिब्यूट setattr से बनाए जाएँ, तो उन्हें भी निकले हुए नाम से ही पढ़ें. instance.__dict__[name] या getattr(instance, name) के जरिए एक्सेस वही तरीका प्रतिध्वनित करता है जिससे आपने उन्हें सेट किया था, और अप्रत्याशित व्यवहार से बचाता है. यदि आपका उपयोग मामला मूलतः key-value स्टोरेज जैसा है, तो API को साफ-साफ रखने और ढांचे को सरल बनाने के लिए डिक्ट-आधारित तरीका अपनाने पर विचार करें.

यह लेख StackOverflow पर एक प्रश्न (लेखक: Javier Olivares) और Richard के उत्तर पर आधारित है।