2025, Oct 20 03:32

Python 3.11 और mypy 1.16 में list जोड़ पर असंगति: कारण, उदाहरण और उपाय

Python 3.11 और mypy 1.16 में list जोड़/concatenate पर टाइप-चेक असंगति; बॉडी में पास, return पर त्रुटि. कारण, न्यूनतम उदाहरण, issues/PR और सुरक्षित unpacking उपाय.

Python 3.11 और mypy 1.16 के साथ काम करते समय एक उलझाने वाली असंगति दिख सकती है: संगत तत्व-प्रकारों वाली सूचियों को जोड़ना (concatenate) एक साधारण अभिव्यक्ति के रूप में टाइप जाँच में पास हो जाता है, लेकिन वही अभिव्यक्ति सीधे return में इस्तेमाल करने पर त्रुटि आती है। देखने में तो तत्व-प्रकारों का सामान्य union पर्याप्त होना चाहिए, फिर भी फ़ंक्शन की सीमा पर mypy असहमत हो जाता है।

न्यूनतम उदाहरण

def combine() -> list[str | int]:
    alpha: list[str | int]
    beta: list[int]
    alpha + beta  # अभिव्यक्ति के रूप में ठीक
    return alpha + beta  # error: Unsupported operand types for + ("list[str | int]" and "list[int]")  [operator]

उलझन यही है कि बिल्कुल वही ऑपरेशन जगह के अनुसार अलग तरह से आँका जा रहा है: बॉडी में स्वीकार्य, लेकिन return की स्थिति में अस्वीकार्य।

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

यह व्यवहार mypy का लंबे समय से चल रहा मुद्दा है। इसे कई बार दर्ज किया गया है और दोबारा उठाया भी गया, पर अब तक सुलझा नहीं है। इसे संबोधित करने के लिए एक ड्राफ्ट pull request मौजूद है। संदर्भ: बग रिपोर्ट https://github.com/python/mypy/issues/3933 और ड्राफ्ट PR https://github.com/python/mypy/pull/19399।

एक संबंधित बात यह है: यदि जोड़ के परिणाम को बिना एनोटेशन वाले अस्थायी वैरिएबल में रखें, तो mypy एक संगत प्रकार का अनुमान लगाता है और फ़ंक्शन साफ़-सुथरे ढंग से लौट जाता है। लेकिन उसी अस्थायी को list[str | int] जैसा ठोस प्रकार देकर annotate करें, तो त्रुटि असाइनमेंट पर दिखेगी, return पर नहीं।

gamma: list[str | int] = []
delta: list[int] = []
merged = gamma + delta
reveal_type(merged)  # list[int | str]
return merged  # ठीक है
merged_explicit: list[str | int] = gamma + delta  # त्रुटि
reveal_type(merged_explicit)  # list[str | int]
return merged_explicit  # ठीक है

इससे स्पष्ट है कि दिक्कत केवल return तक सीमित नहीं है; जब जोड़ का परिणाम किसी स्पष्ट लक्ष्य प्रकार से मिलाया जाता है, तब भी वही समस्या उभरती है। एक संबंधित चर्चा यहाँ है: https://github.com/python/mypy/issues/3351।

व्यावहारिक समाधान

जब तक यह बग upstream में ठीक नहीं होता, सुझाया तरीका है कि सूची जोड़ने की जगह अनपैकिंग का उपयोग करें—mypy ऊपर दिए गए सभी परिदृश्यों में इसे स्वीकार करता है।

def combine_fixed() -> list[str | int]:
    alpha: list[str | int]
    beta: list[int]
    return [*alpha, *beta]  # ठीक है

अगर आप अस्थायी मान रखना चाहें, तो यही पैटर्न वैसा ही काम करता है।

gamma: list[str | int] = []
delta: list[int] = []
epsilon = [*gamma, *delta]
reveal_type(epsilon)  # list[int | str]
return epsilon  # ठीक है

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

टाइप चेकर ऐसे औज़ार हैं जो कोड की बनावट और तर्क पर हमारा सोचने का तरीका प्रभावित करते हैं। इस तरह की सूक्ष्म असंगतियाँ भ्रामक false positives, कोड रिव्यू में अनावश्यक खींचतान, या बेवजह के refactor की वजह बन सकती हैं। मौजूदा सीमाएँ जानना आपको सटीक टाइप रखते हुए कोड भेजने में मदद करता है और असली टाइप त्रुटियों तथा टूल-विशेष विचित्रताओं में फर्क करना आसान बनाता है।

मुख्य बातें

यदि union-टाइप तत्वों वाली सूचियों का जोड़ एक स्वतंत्र अभिव्यक्ति के रूप में पास हो रहा हो, पर return या स्पष्ट रूप से annotate किए गए लक्ष्य को असाइन करते समय असफल हो, तो संभव है कि आप mypy के ज्ञात बग से टकरा रहे हों। समस्या से बचने के लिए [*a, *b] के साथ अनपैकिंग को प्राथमिकता दें, या बिना एनोटेशन वाले अस्थायी में असाइन कर दें ताकि inference काम कर सके। ऊपर दिए गए upstream issue और ड्राफ्ट PR पर नज़र रखें—जब यह मिल जाएगा, तो मूल रूप भी बिना किसी वर्कअराउंड के व्यवहार्य हो सकता है।

यह लेख StackOverflow के प्रश्न (लेखक: Stas Stepanov) और InSync के उत्तर पर आधारित है।