2025, Oct 08 05:31
dict[str, str] को Mapping[str|int, str] क्यों नहीं मानें और क्या करें
Python टाइपिंग में Mapping key invariance से dict[str, str] को Mapping[str|int, str] पास करना mypy/pyright में त्रुटि बनता है; कारण, समाधान और सही एनोटेशन जानें.
एक साधारण dict को ऐसी फ़ंक्शन में पास करना, जो अधिक व्यापक कुंजी प्रकार वाले Mapping की अपेक्षा करती है, ऊपर से सुरक्षित लगता है, लेकिन टाइप चेकर असहमत होते हैं। यहाँ बताया गया है कि mypy 1.16.0 और pyright 1.1.400 इस पैटर्न को क्यों फ़्लैग करते हैं और टूल्स से जूझे बिना आप क्या कर सकते हैं।
समस्या को दोहराना
फ़ंक्शन ऐसे Mapping की अपेक्षा करता है जिसकी कुंजियाँ str या int हो सकती हैं, जबकि कॉल साइट केवल str कुंजियों वाला dict देती है। टाइप चेकर इस असाइनमेंट को अस्वीकार कर देते हैं।
from collections.abc import Mapping
def consume_map(src: Mapping[str | int, str]):
    print(src)
payload = {"a": "b"}
consume_map(payload)
mypy और pyright दोनों रिपोर्ट करते हैं कि dict[str, str], Mapping[str | int, str] को असाइन नहीं किया जा सकता। उदाहरण के लिए, pyright समझाता है कि Mapping के key प्रकार का पैरामीटर invariant है और str, str | int के बराबर नहीं है।
यह क्यों हो रहा है
भले ही Mapping अपरिवर्तनीय है, Python की टाइपिंग में इसकी key प्रकार का पैरामीटर invariant माना जाता है। Mapping कुंजियों को covariant बनाने के प्रस्ताव एक से अधिक बार रखे गए और खारिज कर दिए गए। नतीजतन, टाइप चेकर लगातार यह नियम लागू करते हैं कि Mapping[K, V] तब Mapping[K_sub, V] स्वीकार नहीं करता जब K_sub, K से संकीर्ण हो। हमारे मामले में dict[str, str], Mapping[str | int, str] से संकीर्ण है, इसलिए असाइनमेंट खारिज हो जाता है।
व्यावहारिक समाधान
टाइप चेकर को संतुष्ट करने का सीधा तरीका यह है कि परिभाषा के समय ही डिक्शनरी को व्यापक कुंजी प्रकार के साथ घोषित करें। इससे मंशा स्पष्ट रहती है और फ़ंक्शन सिग्नेचर से मेल खाती है।
from collections.abc import Mapping
def consume_map(src: Mapping[str | int, str]):
    print(src)
store: dict[str | int, str] = {"a": "b"}
consume_map(store)
यदि आपका लक्ष्य या तो पूरी तरह string-कुंजी वाले मैपिंग्स या पूरी तरह int-कुंजी वाले मैपिंग्स स्वीकार करना है, तो पैरामीटर प्रकार में Mapping के अलग-अलग संस्करणों का यूनियन लिखकर उस मंशा को स्पष्ट करें: Mapping[str, str] | Mapping[int, str]। जब आपको वास्तव में दोनों तरह की कुंजियों का मिश्रण एक ही मैपिंग में नहीं चाहिए, तब यह एक उपयोगी वर्कअराउंड हो सकता है।
यह क्यों मायने रखता है
यह मान लेना कि अपरिवर्तनशीलता का अर्थ सुरक्षित covariance है, समीक्षा के समय या CI में उलझाने वाली त्रुटियों तक ले जा सकता है। मौजूदा टाइपिंग नियम स्पष्ट हैं: Mapping का key पैरामीटर invariant है। mypy और pyright दोनों इसे लगातार लागू करते हैं। यह बात पहले से जानना सही एनोटेशन रणनीति चुनने में मदद करता है और आपके कोडबेस को suppressions और एड-हॉक कास्ट्स से मुक्त रखता है।
मुख्य बातें
ऐसी फ़ंक्शन सीमाएँ डिज़ाइन करते समय जो मैपिंग्स स्वीकार करती हैं, कुंजी प्रकारों के बारे में स्पष्ट रहें। अगर उपभोक्ता को सच में Mapping[str | int, str] चाहिए, तो उत्पादक को उसी व्यापक कुंजी प्रकार के साथ घोषित करें। अगर आपको या तो string-कुंजी वाले या int-कुंजी वाले मैप्स संभालने हैं, तो इसे Mapping[str, str] और Mapping[int, str] के यूनियन के रूप में साफ़-साफ़ लिखें। सबसे अहम, यह न मानें कि अपरिवर्तनशीलता यहाँ variance की कहानी बदल देती है—Mapping कुंजियों को covariant बनाने के प्रस्ताव खारिज किए जा चुके हैं, इसलिए टूल्स invariant नियम ही लागू करेंगे।
यह लेख StackOverflow पर एक प्रश्न (लेखक: Kerrick Staley) और Anerdw के उत्तर पर आधारित है।