2025, Oct 17 07:34
Python के sum() में _Addable का मतलब क्या है और स्ट्रिंग्स क्यों निषिद्ध हैं
Python के sum() में _Addable/SupportsAdd क्या है, typeshed इसे कैसे परिभाषित करता है, और स्ट्रिंग्स पर रोक क्यों, जानें. सही उपाय: ''.join(); start के सुझाव.
IDE में Python के sum() को देखते हुए, _Addable जैसे रहस्यमय टाइप नाम आसानी से नज़र आ जाते हैं. यह फ़ंक्शन स्पष्ट तौर पर संख्याएँ जोड़ता है और दिए गए start मान के साथ सूचियों (lists) को जोड़ भी सकता है, लेकिन स्ट्रिंग्स + को सपोर्ट करने के बावजूद उनके साथ काम करने से इनकार करता है. यहाँ “addable” का असल मतलब क्या है, और sum(['a', 'b'], '') रनटाइम पर क्यों फेल हो जाता है?
स्थिति को दोहराकर देखना
पहले वह व्यवहार जो सहज लगता है: संख्याओं का जोड़ और start मान के जरिए सूचियों का संयोजन.
a_numbers = list(range(1, 4))
b_numbers = list(range(4, 7))
sum(a_numbers)  # 6
nested_lists = [a_numbers, b_numbers]
sum(nested_lists, [])  # [1, 2, 3, 4, 5, 6]
अब हैरान करने वाला मामला. स्ट्रिंग्स में जोड़ लागू होता है, फिर भी sum उन्हें अस्वीकार कर देता है, चाहे start मान दिया गया हो.
sum(["x", "y"], "")
TypeError: sum() can't sum strings [use ''.join(seq) instead]
_Addable कहाँ से आया
संकेत Python के रनटाइम में नहीं, बल्कि उसकी टाइपिंग जानकारी में है. जो टाइप हिंट आपने देखा, वह SupportsAdd नामक एक प्रोटोकॉल से जुड़ा है, जिसे _typeshed नाम के केवल-स्टब सहायक मॉड्यूल में परिभाषित किया गया है. यह मॉड्यूल typeshed का हिस्सा है—वही साझा स्रोत जहाँ से टाइप चेकर और लैंग्वेज सर्वर स्टैंडर्ड लाइब्रेरी और आम पैकेजों के लिए टाइप जानकारी लेते हैं. यह .pyi स्टब्स के रूप में आता है और रनटाइम पर इम्पोर्ट नहीं किया जा सकता. यानी, import _typeshed आपका प्रोग्राम चलने पर काम नहीं करेगा; यह सिर्फ स्थिर विश्लेषण (static analysis) के लिए मौजूद है.
SupportsAdd बहुत सरल है. यह ऐसा प्रोटोकॉल है जो बस कहता है: “इस टाइप में __add__ मौजूद है”.
class AddProto(Protocol[_U_contra, _V_co]):
    def __add__(self, other: _U_contra, /) -> _V_co: ...
इस रूप में SupportsAdd[Any, Any] का मतलब है “कोई भी क्लास जो __add__ परिभाषित करती है”. आपने जो _Addable टाइप वेरिएबल्स देखे, वे इसी प्रोटोकॉल से बंधे हैं.
AddableA = TypeVar("AddableA", bound=AddProto[Any, Any])
AddableB = TypeVar("AddableB", bound=AddProto[Any, Any])
sum() का टाइप कैसे परिभाषित है
typeshed में sum के लिए बना स्टब कुछ आम स्थितियों को समेटने के लिए ओवरलोड किया गया है. इसमें booleans और literal integers के लिए विशेष केस है, और “addable” टाइप्स के लिए एक सामान्य केस.
AddableA = TypeVar("AddableA", bound=AddProto[Any, Any])
AddableB = TypeVar("AddableB", bound=AddProto[Any, Any])
@type_check_only
class _SumNoDefaultProto(AddProto[Any, Any], SupportsRAdd[int, Any], Protocol): ...
_SumNoDefaultT = TypeVar("_SumNoDefaultT", bound=_SumNoDefaultProto)
@overload
def sum(items: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ...
@overload
def sum(items: Iterable[_SumNoDefaultT], /) -> _SumNoDefaultT | Literal[0]: ...
@overload
def sum(items: Iterable[AddableA], /, start: AddableB) -> AddableA | AddableB: ...
यह बताता है कि sum “addable” तत्वों के iterable पर काम करता है, और चाहें तो start मान लिया जा सकता है. साथ ही यह भी स्पष्ट करता है कि यदि आप start नहीं देते, तो sum मानो 0 से शुरू होता है—इसी वजह से ऐसा टाइप जिसके साथ int नहीं जोड़ा जा सकता, फेल होगा, जब तक कि आपका टाइप वह ऑपरेशन सपोर्ट न करे. स्थिर टाइप चेकर इसे इन ओवरलोड्स के जरिए दिखाते हैं.
तो फिर sum() स्ट्रिंग्स को क्यों ठुकराता है?
मूल बात यह है: Python की स्थिर टाइपिंग हर रनटाइम व्यवहारिक बारीकी को कैप्चर करने की कोशिश नहीं करती. स्ट्रिंग्स को जोड़ने पर रोक एक रनटाइम नियम है, जो इम्प्लिमेंटेशन लागू करता है. typeshed में sum के टाइप्स इस नियम को मॉडल नहीं करते, इसलिए टाइप चेकर इसके बारे में चेतावनी देने के लिए बाध्य नहीं हैं. यही कारण है कि कई टूल्स में sum(["x", "y"], "") बिना त्रुटि टाइप‑चेक हो जाता है, पर चलाने पर असफल होता है.
संक्षेप में, यहाँ “addable” का मतलब टाइपिंग के संदर्भ में सिर्फ इतना है कि “__add__ मौजूद है”, न कि “sum इसे हर हाल में स्वीकार करेगा.” स्ट्रिंग्स एक उल्लेखनीय अपवाद हैं, जिन्हें sum रनटाइम पर स्पष्ट रूप से निषिद्ध करता है.
स्ट्रिंग्स के लिए सही तरीका
रनटाइम त्रुटि संदेश स्ट्रिंग्स के लिए इच्छित पैटर्न खुद बता देता है: join का उपयोग करें.
"".join(["x", "y"])  # "xy"
कस्टम टाइप्स और sum()
कोई भी यूज़र-परिभाषित क्लास जो __add__ लागू करती है, SupportsAdd प्रोटोकॉल में फिट बैठती है और इसलिए sum के तत्व-टाइप के रूप में स्थिर टाइप चेकर के लिए मान्य है. यदि आप start मान दिए बिना वाले रूप पर निर्भर करते हैं, तो याद रखें कि sum मानो 0 से शुरू होता है; ऐसे में आपके टाइप को 0 + instance सपोर्ट करना होगा. अगर ऐसा नहीं है, तो अपने ही टाइप का start मान दें ताकि पहली जोड़ स्पष्ट रूप से परिभाषित हो.
यह क्यों मायने रखता है
यह उदाहरण Python के रनटाइम अर्थविज्ञान और उसके स्थिर टाइप सिस्टम के बीच की सीमा रेखा को अच्छी तरह दिखाता है. stdlib के typeshed स्टब्स संपादकों और टाइप चेकरों को मजबूत दिशा‑निर्देश देते हैं, लेकिन CPython द्वारा लागू हर विशेष केस को एन्कोड नहीं करते. यह जानना कि _typeshed सिर्फ टाइप‑चेकिंग संसाधन है, SupportsAdd का मतलब __add__ की मौजूदगी है, और sum में स्ट्रिंग जैसी रनटाइम विशेष पाबंदियाँ टाइप स्तर पर जरूरी नहीं कि दर्शाई गई हों—ये बातें अप्रत्याशित आश्चर्यों से बचाती हैं और टाइप हिंट्स को रोज़ाना के काम में ज्यादा उपयोगी बनाती हैं.
मुख्य बातें
जब आप sum के टाइप हिंट्स में _Addable देखें, तो उसे “जिसमें __add__ हो” के तौर पर पढ़ें—जैसा _typeshed में SupportsAdd प्रोटोकॉल परिभाषित करता है. उम्मीद रखें कि स्थिर टाइप चेकर ऐसे किसी भी टाइप पर sum को स्वीकार करेंगे, लेकिन यह भी याद रखें कि रनटाइम अतिरिक्त नियम लागू कर सकता है, जैसे स्ट्रिंग्स पर रोक. स्ट्रिंग जोड़ने के लिए ''.join(...) का उपयोग करें, और अपनी क्लासों के साथ sum चलाते समय, अगर integers से जोड़ना आपके ऑब्जेक्ट्स के लिए अर्थपूर्ण नहीं है, तो उसी टाइप का start मान दें.
यह लेख StackOverflow के प्रश्न (लेखक: tarheeljks) और STerliakov के उत्तर पर आधारित है.