1 पॉइंट द्वारा GN⁺ 1 시간 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • PEP 661 None के मान्य value होने वाली स्थितियों में अलग से पहचाने जा सकने वाले sentinel value बनाने के लिए Python built-in callable sentinel() और C API PySentinel_New() का प्रस्ताव करता है
  • मौजूदा _sentinel = object() idiom में function signature में repr लंबा और अस्पष्ट होता है, और स्पष्ट type signature, copy, तथा pickling में समस्याएँ आ सकती हैं
  • sentinel('MISSING') कॉल छोटा repr वाला एक नया unique object बनाती है, और वही sentinel साझा करने के लिए उसे MISSING = sentinel('MISSING') की तरह किसी variable में assign करके स्पष्ट रूप से reuse करना होगा
  • sentinel की तुलना is से करने की सिफारिश की जाती है और यह truthy evaluate होता है, copy.copy() और copy.deepcopy() वही object लौटाते हैं, और अगर module से नाम के आधार पर import किया जा सके तो pickling के बाद भी identity बनी रहती है
  • type system sentinel को खुद type expression में इस्तेमाल करने देता है, जैसे int | MISSING, और नवीनतम आधिकारिक दस्तावेज़ Python 3.15 के [sentinel](<https://docs.python.org/3.15/library/functions.html#sentinel "(in Python v3.15>)") docs में हैं

परिचय की पृष्ठभूमि

  • एक unique placeholder value यानी sentinel value का उपयोग function argument न दिए जाने पर default value, खोज विफल होने को दिखाने वाले return value, या missing data को दिखाने वाले value के रूप में किया जाता है
  • Python में आम तौर पर इस काम के लिए विशेष value None होती है, लेकिन जिन संदर्भों में None खुद एक valid value है, वहाँ None से अलग एक अलग sentinel value की जरूरत होती है
  • मई 2021 में python-dev mailing list पर traceback.print_exception में इस्तेमाल होने वाले sentinel value को बेहतर तरीके से implement करने पर चर्चा हुई
  • मौजूदा implementation ने सामान्य idiom _sentinel = object() का उपयोग किया था, लेकिन उसका repr बहुत लंबा और कम जानकारी वाला था, जिससे function signature पढ़ना कठिन हो जाता था
    &gt;&gt;&gt; help(traceback.print_exception)  
    Help on function print_exception in module traceback:  
    
    print_exception(exc, /, value=&lt;object object at  
    0x000002825DF09650&gt;, tb=&lt;object object at 0x000002825DF09650&gt;,  
    limit=None, file=None, chain=True)  
    
  • चर्चा के दौरान मौजूदा sentinel implementations की अन्य समस्याएँ भी सामने आईं
    • कुछ sentinel के लिए unique type नहीं होता, इसलिए sentinel को default value के रूप में इस्तेमाल करने वाले functions के लिए स्पष्ट type signature परिभाषित करना कठिन होता है
    • copy के बाद अलग instance बन जाता है, जिससे is तुलना विफल होने जैसी अप्रत्याशित behavior हो सकती है
    • कुछ सामान्य idiom में pickling और unpickling के बाद भी इसी तरह की समस्याएँ होती हैं
  • Victor Stinner ने Python standard library में इस्तेमाल होने वाले sentinel value की सूची दी, और यह पुष्टि हुई कि standard library के भीतर भी कई implementation तरीके उपयोग में हैं, तथा उनमें से कई में ऊपर बताई गई एक या अधिक समस्याएँ हैं
  • discuss.python.org पर हुए मतदान में 39 वोटों के आधार पर कोई स्पष्ट निष्कर्ष नहीं निकल सका
    • 40% ने “मौजूदा स्थिति ठीक है और consistency की जरूरत नहीं है” चुना
    • बहुमत ने एक या अधिक standardized solution चुने
    • 37% ने “एक नई dedicated sentinel factory/class/metaclass को लगातार इस्तेमाल किया जाए और standard library में public रूप से उपलब्ध कराया जाए” वाला विकल्प चुना
  • इन मिले-जुले परिणामों की वजह से PEP लिखा गया, और इस निष्कर्ष पर पहुँचा गया कि standard library के भीतर और बाहर दोनों जगह उपयोगी एक सरल और अच्छा standard library implementation फायदेमंद होगा
  • standard library में मौजूद सभी sentinel को इसी तरीके से बदलना अनिवार्य नहीं है, और यह संबंधित maintainer के विवेक पर छोड़ा गया है
  • PEP दस्तावेज़ एक historical document है, और नवीनतम आधिकारिक दस्तावेज़ Python 3.15 के [sentinel](<https://docs.python.org/3.15/library/functions.html#sentinel "(in Python v3.15>)") docs में हैं

डिज़ाइन मानदंड

  • sentinel object is operator से तुलना करने पर हमेशा स्वयं के समान होना चाहिए और किसी भी दूसरे object के समान नहीं होना चाहिए
  • sentinel object बनाना सरल और सहज एक लाइन का code होना चाहिए
  • जितनी जरूरत हो उतनी अलग-अलग sentinel value आसानी से define की जा सकें
  • sentinel object का repr छोटा और स्पष्ट होना चाहिए
  • sentinel के लिए स्पष्ट type signature इस्तेमाल किया जा सके
  • copy के बाद भी सही तरह से काम करना चाहिए, और pickling तथा unpickling के समय behavior पूर्वानुमेय होना चाहिए
  • यह CPython 3.x और PyPy3 पर काम करना चाहिए, और संभव हो तो अन्य Python implementations पर भी
  • implementation और usage दोनों यथासंभव सरल और सहज होने चाहिए, ताकि Python सीखते समय यह एक और विशेष concept बनकर बोझ न बने
  • standard library sentinels या sentinel जैसे PyPI package implementations पर निर्भर नहीं हो सकती, इसलिए standard library के भीतर इस्तेमाल की जा सकने वाली implementation की जरूरत है

sentinel() विनिर्देशन

  • नया built-in callable object sentinel जोड़ा गया है
    >>> MISSING = sentinel('MISSING')  
    >>> MISSING  
    MISSING  
    
  • sentinel() एक ही positional-only argument name लेता है, और name का str होना अनिवार्य है
  • string के अलावा कोई और मान देने पर TypeError उठता है
  • name का उपयोग sentinel के नाम और repr के रूप में किया जाता है
  • sentinel object में दो public attributes होते हैं
    • __name__: sentinel का नाम
    • __module__: उस module का नाम जहाँ sentinel() को कॉल किया गया था
  • sentinel को subclass नहीं किया जा सकता
  • sentinel(name) को हर बार कॉल करने पर एक नया sentinel object लौटता है
  • अगर एक ही sentinel को कई जगह इस्तेमाल करना हो, तो मौजूदा MISSING = object() idiom की तरह उसे किसी variable में assign करके उसी object को explicitly reuse करना चाहिए
    MISSING = sentinel('MISSING')  
    
    def read_value(default=MISSING):  
        ...  
    
  • कोई मान sentinel है या नहीं, यह जांचने के लिए None की तरह is operator का उपयोग करने की सिफारिश की जाती है
  • == comparison भी अपेक्षित तरीके से काम करता है और केवल स्वयं से तुलना होने पर True लौटाता है
  • if value is MISSING: जैसी identity check आम तौर पर if value: या if not value: जैसी boolean check से अधिक उपयुक्त होती है
  • sentinel object truthy होते हैं, और boolean evaluation का परिणाम True होता है
    • यह arbitrary class के default behavior और Ellipsis के boolean value जैसा ही है
    • यह falsy None से अलग है
  • copy.copy() या copy.deepcopy() से sentinel object की copy करने पर वही object लौटता है
  • जो sentinel अपने define किए गए module से नाम के जरिए import किया जा सकता है, वह standard pickle mechanism के अनुसार pickling और unpickling के बाद भी identity बनाए रखता है
    MISSING = sentinel('MISSING')  
    assert pickle.loads(pickle.dumps(MISSING)) is MISSING  
    
  • sentinel() sentinel बनाते समय calling module को __module__ attribute में रिकॉर्ड करता है
  • pickling में sentinel को उसके module और नाम से रिकॉर्ड किया जाता है, और unpickling में module import करने के बाद नाम के जरिए sentinel लिया जाता है
  • जो sentinel module और नाम के जरिए import नहीं किए जा सकते, जैसे local scope में बनाए गए sentinel जिन्हें module global या class attribute के matching name पर assign नहीं किया गया हो, उन्हें pickle नहीं किया जा सकता
  • sentinel object का repr, sentinel() को दिया गया name होता है, और इसमें कोई implicit module qualifier नहीं जुड़ता
  • अगर qualified repr चाहिए, तो उसे नाम में explicitly शामिल करना होगा
    >>> MyClass_NotGiven = sentinel('MyClass.NotGiven')  
    >>> MyClass_NotGiven  
    MyClass.NotGiven  
    
  • sentinel object के order comparison परिभाषित नहीं हैं
  • sentinel weakref को support नहीं करते

टाइपिंग

  • typed Python code में sentinel के उपयोग को स्पष्ट और सरल बनाने के लिए type system में sentinel object के लिए special handling जोड़ी गई है
  • sentinel object को type expression के भीतर स्वयं को दर्शाने वाले value के रूप में उपयोग किया जा सकता है
  • यह मौजूदा type system में None को संभालने के तरीके के समान है
    MISSING = sentinel('MISSING')  
    
    def foo(value: int | MISSING = MISSING) -> int:  
        ...  
    
  • type checker को NAME = sentinel('NAME') रूप वाले sentinel creation को नए sentinel object के निर्माण के रूप में पहचानना चाहिए
  • अगर sentinel() में दिया गया नाम assignment target के नाम से मेल नहीं खाता, तो type checker को error देनी चाहिए
  • इस syntax से परिभाषित sentinel को type expression में उपयोग किया जा सकता है
  • संबंधित sentinel type ऐसा fully static type दर्शाता है जिसका एकमात्र member वही sentinel object होता है
  • type checker को is और is not operators का उपयोग करके sentinel शामिल union type narrowing को support करना चाहिए
    from typing import assert_type  
    
    MISSING = sentinel('MISSING')  
    
    def foo(value: int | MISSING) -> None:  
        if value is MISSING:  
            assert_type(value, MISSING)  
        else:  
            assert_type(value, int)  
    
  • runtime implementation में type expression usage को support करने के लिए __or__ और __ror__ methods होने चाहिए, और ये methods typing.Union object लौटाते हैं
  • Typing Council ने इस प्रस्ताव के typing-संबंधित हिस्सों का समर्थन किया है

C API

  • C extension में भी sentinel उपयोगी हो सकते हैं, इसलिए दो नए C API functions प्रस्तावित हैं
  • PyObject *PySentinel_New(const char *name, const char *module_name) नया sentinel object बनाता है
  • bool PySentinel_Check(PyObject *obj) जांचता है कि object sentinel है या नहीं
  • किसी specific sentinel की जांच करते समय C code == operator का उपयोग कर सकता है

संगतता और सुरक्षा

  • नया built-in नाम जोड़ने पर, वह code जो अभी bare name sentinel के लिए NameError आने की अपेक्षा करता है, अब वही परिणाम नहीं देखेगा
  • नया built-in नाम जोड़ते समय यह एक सामान्य compatibility consideration है
  • पहले से मौजूद local, global, import किए गए नाम sentinel पर इसका असर नहीं पड़ेगा
  • जो code पहले से sentinel नाम का उपयोग करता है, उसे नए built-in object के उपयोग के अनुरूप समायोजित करना पड़ सकता है, और built-in नाम से टकराव की चेतावनी देने वाले linter से नई warning मिल सकती है
  • नए built-in feature के लिए सामान्य documentation तरीके जैसे docstring, library documentation, और “What’s New” section को पर्याप्त माना गया है
  • इस प्रस्ताव का कोई security impact नहीं माना गया है

संदर्भ implementation और backport

  • संदर्भ implementation CPython pull request [10] के रूप में उपलब्ध है
  • पिछला संदर्भ implementation अलग GitHub repository [7] में है
  • इच्छित व्यवहार का स्केच इस प्रकार है
    class sentinel:  
        &quot;&quot;&quot;Unique sentinel values.&quot;&quot;&quot;  
    
        __slots__ = (&quot;__name__&quot;, &quot;_module_name&quot;)  
    
        def __init_subclass__(cls):  
            raise TypeError(&quot;type &#039;sentinel&#039; is not an acceptable base type&quot;)  
    
        def __init__(self, name, /):  
            if not isinstance(name, str):  
                raise TypeError(&quot;sentinel name must be a string&quot;)  
            self.__name__ = name  
            self._module_name = sys._getframemodulename(1)  
    
        @property  
        def __module__(self):  
            return self._module_name  
    
        def __repr__(self):  
            return self.__name__  
    
        def __reduce__(self):  
            return self.__name__  
    
        def __copy__(self):  
            return self  
    
        def __deepcopy__(self, memo):  
            return self  
    
        def __or__(self, other):  
            return typing.Union[self, other]  
    
        def __ror__(self, other):  
            return typing.Union[other, self]  
    
    • typing-extensions module में backport मौजूद है, लेकिन यह अभी PEP के वर्तमान iteration के व्यवहार से पूरी तरह मेल नहीं खाता

अस्वीकृत विकल्प

  • NotGiven = object() का उपयोग

    • इस तरीके में PEP के design criteria में बताई गई सभी कमियां हैं
    • इसका repr लंबा और अस्पष्ट है, type signature को स्पष्ट बनाना कठिन है, और copy या pickling से जुड़ी समस्याएं हो सकती हैं
  • MISSING या Sentinel जैसा एकल नया sentinel मान जोड़ना

    • अगर एक ही मान कई जगहों पर कई उद्देश्यों के लिए इस्तेमाल हो, तो हर use case में यह भरोसे के साथ कहना मुश्किल हो सकता है कि वह मान स्वयं वैध मान नहीं होगा
    • अलग-अलग समर्पित sentinel मानों को संभावित edge case पर विचार किए बिना अधिक भरोसे के साथ इस्तेमाल किया जा सकता है
    • sentinel मानों को उपयोग के context के अनुरूप अर्थपूर्ण नाम और repr देना संभव होना चाहिए
    • वोटिंग में इस विकल्प को केवल 12% समर्थन मिला, इसलिए यह बहुत अलोकप्रिय था
  • मौजूदा Ellipsis sentinel मान का उपयोग

    • Ellipsis मूल रूप से इस उद्देश्य के लिए नहीं बनाया गया था
    • pass की जगह खाली class या function block परिभाषित करने में इसका उपयोग बढ़ा है, लेकिन यह समर्पित अलग sentinel मानों जितना हर स्थिति में भरोसेमंद नहीं है
  • एकल-मान Enum का उपयोग

    • प्रस्तावित idiom इस प्रकार है
    class NotGivenType(Enum):  
      NotGiven = &#039;NotGiven&#039;  
      NotGiven = NotGivenType.NotGiven  
    
  • इसमें दोहराव बहुत अधिक है, और repr &lt;NotGivenType.NotGiven: &#039;NotGiven&#039;&gt; की तरह बहुत लंबा है
  • छोटा repr परिभाषित किया जा सकता है, लेकिन इससे code और दोहराव और बढ़ता है
  • वोटिंग के 9 विकल्पों में यह एकमात्र विकल्प था जिसे एक भी वोट नहीं मिला, इसलिए यह सबसे कम लोकप्रिय था
  • sentinel class decorator

    • प्रस्तावित idiom इस प्रकार है
      @sentinel  
      class NotGivenType: pass  
      NotGiven = NotGivenType()  
      
    • decorator का implementation स्वयं सरल और स्पष्ट हो सकता है, लेकिन यह idiom बहुत लंबा, दोहरावपूर्ण और याद रखने में कठिन है
  • class object का उपयोग

    • class मूल रूप से singleton होती है, इसलिए उसे sentinel मान की तरह इस्तेमाल करने का विचार संभव है
    • इसका सबसे सरल रूप इस प्रकार है
      class NotGiven: pass  
      
      • स्पष्ट repr पाने के लिए metaclass या class decorator की आवश्यकता होती है
      class NotGiven(metaclass=SentinelMeta): pass  
      
      @Sentinel  
      class NotGiven: pass  
      
    • class का इस तरह उपयोग असामान्य है, इसलिए यह भ्रम पैदा कर सकता है
    • बिना टिप्पणी के code का उद्देश्य समझना कठिन है, और sentinel के callable हो जाने जैसे अप्रत्याशित तथा अवांछित व्यवहार उत्पन्न होते हैं
  • implementation के बिना केवल अनुशंसित standard idiom परिभाषित करना

    • अधिकांश सामान्य मौजूदा idiom में महत्वपूर्ण कमियां हैं
    • अब तक ऐसा कोई स्पष्ट और संक्षिप्त idiom नहीं मिला जो इन कमियों से बच सके
    • संबंधित वोटिंग में idiom recommendation वाला विकल्प अलोकप्रिय था, और सबसे अधिक वोट पाने वाला विकल्प भी केवल 25% तक पहुंचा
  • नया standard library module उपयोग करना

    • शुरुआती draft में नए sentinels या sentinellib module में Sentinel class जोड़ने का प्रस्ताव था
    • केवल एक public callable object के लिए नया module जोड़ना अनावश्यक है
    • module उपयोग करने से functionality का इस्तेमाल मौजूदा object() idiom की तुलना में कम सुविधाजनक हो जाता है
    • Steering Council ने भी विशेष रूप से सिफारिश की कि इसे object() जितना आसान इस्तेमाल होने वाला built-in feature बनाया जाए
    • sentinels नाम पहले से सक्रिय रूप से उपयोग किए जा रहे PyPI package से टकराता है, और built-in feature बनाने से यह naming समस्या टल जाती है
  • module-स्तरीय sentinel name registry का उपयोग

    • शुरुआती draft में sentinel नामों को module के भीतर unique बनाने का प्रस्ताव था
    • इस design में, उसी module में sentinel("MISSING") को बार-बार call करने पर, module नाम और sentinel नाम को key मानने वाली process-wide global registry के जरिए वही object लौटाया जाता है
    • यह व्यवहार अत्यधिक implicit होने के कारण अस्वीकार किया गया
    • अगर shared sentinel चाहिए, तो मौजूदा MISSING = object() की तरह एक को स्पष्ट रूप से define करके नाम से reuse किया जा सकता है
    • local scope में call या दोहराव के हर बार नया sentinel चाहिए हो सकता है, इसलिए sentinel(name) को बार-बार call करने पर object() की बार-बार call की तरह अलग-अलग object बनने चाहिए
    • registry हटाने से implementation और mental model दोनों सरल हो जाते हैं, और केवल यह नियम बचता है कि sentinel(name) repr के रूप में name वाला नया unique object बनाता है
  • module नाम का स्वतः पता लगाना या पास करना

    • शुरुआती draft में registry-आधारित design को support करने के लिए वैकल्पिक module_name argument प्रस्तावित था
    • registry हटने के बाद public module_name argument की अब मुख्य प्रस्ताव में आवश्यकता नहीं रही
    • implementation, TypeVar की तरह, calling module को अंदरूनी रूप से रिकॉर्ड करता है ताकि pickling import किए जा सकने वाले sentinel को module और नाम के आधार पर serialize कर सके
    • अंदरूनी module नाम sentinel के repr को प्रभावित नहीं करता
    • अगर module नाम या class नाम शामिल वाला repr चाहिए, तो sentinel("mymodule.MISSING") की तरह उसे एकल name argument में स्पष्ट रूप से शामिल किया जा सकता है
  • repr customization की अनुमति

    • इसका लाभ यह था कि मौजूदा sentinel मानों को repr बदले बिना इस तरीके में migrate किया जा सकता था
    • लेकिन अतिरिक्त जटिलता को उचित न मानते हुए इसे बाहर रखा गया
  • boolean evaluation customization की अनुमति

    • चर्चा में sentinel को स्पष्ट रूप से truthy, falsy, या bool conversion-असमर्थ बनाने का विकल्प विचाराधीन था
    • कुछ third-party sentinel अपने public API के हिस्से के रूप में falsy व्यवहार प्रदान करते हैं
    • कई प्रतिभागियों ने माना कि boolean context में exception उठाना identity check को बेहतर ढंग से लागू कर सकता है
    • PEP ने सामान्य object के default truthy व्यवहार को बनाए रखते हुए और identity check की सिफारिश करते हुए शुरुआती प्रस्ताव को सरल रखा
    • custom boolean व्यवहार पर बाद में विचार किया जा सकता है, यदि यह तय हो कि इसके लिए अतिरिक्त API और typing जटिलता स्वीकार्य है
  • type annotation में typing.Literal का उपयोग

    • चर्चा में कई लोगों ने यह सुझाव दिया था, और PEP ने भी शुरुआत में यही तरीका अपनाया था
    • लेकिन Literal["MISSING"] sentinel मान MISSING के forward reference के बजाय string मान "MISSING" की ओर इशारा करता है, इसलिए यह भ्रम पैदा कर सकता है
    • bare name उपयोग का सुझाव भी चर्चा में अक्सर दिया गया
    • bare name तरीका None द्वारा स्थापित precedent और परिचित pattern का पालन करता है, इसमें import की जरूरत नहीं होती, और यह काफी छोटा है

अतिरिक्त उपयोग निर्देश

  • class scope में sentinel परिभाषित करते समय, नाम टकराव से बचने के लिए, या जब qualified repr अधिक स्पष्ट हो, तब इच्छित qualified name को स्पष्ट रूप से पास करना चाहिए
    &gt;&gt;&gt; class MyClass:  
    ...    NotGiven = sentinel(&#039;MyClass.NotGiven&#039;)  
    &gt;&gt;&gt; MyClass.NotGiven  
    MyClass.NotGiven  
    
  • function या method के अंदर sentinel बनाना अनुमत है
  • sentinel() की हर call पर अलग object बनता है, इसलिए local scope में बनाए गए sentinel उस scope में object() call से बने मान की तरह व्यवहार करते हैं
  • NotImplemented का boolean मान True है, लेकिन Python 3.9 से इसका यह उपयोग deprecated है और deprecation warning उत्पन्न करता है
  • यह deprecation, bpo-35712 [8] में वर्णित NotImplemented की विशेष समस्या के कारण है
  • यदि कई संबंधित sentinel मान परिभाषित करने हों या उनके बीच क्रम परिभाषित करना हो, तो Enum या इसी तरह का तरीका उपयोग करना चाहिए
  • ऐसे sentinel की typing के बारे में typing-sig mailing list [9] में कई विकल्पों पर चर्चा की गई है

1 टिप्पणियां

 
GN⁺ 1 시간 전
Lobste.rs की राय
  • चुना गया नाम मतलब के हिसाब से बहुत संकीर्ण लगता है, इसलिए अजीब लगता है
    सिर्फ नाम देखें तो unique symbol जैसी कोई चीज़ ज़्यादा लचीला primitive लगती। व्यवहार में यह लगभग symbol की तरह काम करेगा, इसलिए उस तरह इस्तेमाल किया जा सकता है, लेकिन इसका नाम “Sentinels” रखना अटपटा है। शायद Lisp की आदत होने की वजह से ऐसा लग रहा हो

    • लगता है लक्ष्य यह है कि SENTINEL_A, SENTINEL_B से अलग type हो, ताकि पूछा जा सके कि कोई value is_a SENTINEL_A है या नहीं
      Ruby के symbol ऐसे काम नहीं करते: :beef.is_a? :droog.class #=> true
    • Lisp वाली सोच सही है। यह मानकर चला जा रहा है कि व्यापक उपयोग वांछनीय है और वही हल की जाने वाली समस्या है, लेकिन Python में Lisp symbols के ज़्यादातर use cases के लिए पहले से Literal और literal strings मौजूद हैं
      इन्हें named sentinel इसलिए कहा जा रहा है क्योंकि sentinel values Python में एक आम concept और pattern हैं, और sentinels का मकसद उसी pattern के उपयोग से पैदा होने वाली कुछ समस्याओं को सीमित दायरे में हल करना है। “Motivation” और “Rationale” सेक्शन में यही बताया गया है
      साथ ही, sentinel में value semantics नहीं होती, इसलिए एक ही नाम वाले दो sentinel भी अलग values होंगे और एक-दूसरे के बराबर नहीं होंगे। इसलिए यह symbol की तरह काम नहीं करते और उन्हें उस तरह इस्तेमाल भी नहीं करना चाहिए
  • named arguments के default value वाले मसले में, Typst में none के साथ सिर्फ auto value जोड़ देने से लगभग हर वांछित named-argument interface व्यक्त किया जा सकता है
    सिर्फ none ज़्यादातर named-argument defaults के लिए अर्थपूर्ण नहीं होता। none default return value के रूप में तो अच्छा है, लेकिन function argument के रूप में यह कई बार संज्ञा की तरह सही मतलब नहीं देता। matrix(axes=None) का मतलब axes हटाना है, या उन्हें हमेशा की तरह बनाए रखना है, यह अस्पष्ट है। none पास करना और कुछ भी पास न करना अलग है या नहीं, यह भी साफ़ नहीं है। अगर parameter की मौजूदगी को अलग दिखाने के लिए multiple dispatch की ओर जाएँ, तो उस parameter के व्यवहार को document करने की केंद्रीय जगह ही खो जाती है
    auto एक बेहतरीन default value है, क्योंकि इसका सीधा अर्थ है “उपलब्ध जानकारी के आधार पर उचित तरीके से संभालो।” auto | none signature को ज़्यादा स्पष्ट boolean की तरह इस्तेमाल किया जा सकता है, और T | auto | none यह काफ़ी जानकारी देता है कि function value का कैसे उपयोग करेगा। उदाहरण के लिए, अगर T color है, तो auto संभवतः white/black जैसे default चुनेगा या parent से inherit करेगा, T रंग को स्पष्ट रूप से सेट करेगा, और none संदर्भ के अनुसार या तो रंग बिल्कुल सेट नहीं करेगा या उसे transparent की तरह मानेगा

  • दिलचस्प है, और जिज्ञासा है कि कुछ packages की semantics कैसे बदलेंगी। उदाहरण के लिए, Item | None लौटाने के बजाय नीचे जैसा लिखा जा सकता है

    NOT_FOUND = sentinel("NOT_FOUND")  
    def get_item(iid: str) -> Item | NOT_FOUND: ...  
    

    बेशक, कई sentinel के ज़रिए अतिरिक्त अर्थ भी जोड़े जा सकते हैं। पहले भी यह संभव था, लेकिन documentation में इसके लिए कोई “officially recommended” तरीका नहीं था। यह package authors को दूसरी दिशा में ले जा सकता है

    MISSING_ID = sentinel("MISSING_ID")  
    MISSING_VALUE = sentinel("MISSING_VALUE")
    
    def get_item(iid: str) -> Item | MISSING_ID | MISSING_VALUE: ...  
    

    उदाहरण थोड़ा बनावटी है, लेकिन इस मामले में यह अलग किया जा सकता है कि मौजूदा ID है पर उससे जुड़ी value नहीं है, या ऐसी ID ही मौजूद नहीं है इसलिए विफलता हुई। शायद “Pythonic” तरीका exception का उपयोग करना होगा, लेकिन यह आम Python लिखने की तुलना में ज़्यादा functional approach जैसा लगता है

    • यह पहले dummy class बनाकर और module-दर-module instantiate किए जाने वाले singleton को ज़्यादा साफ़ तरीके से लिखने जैसा लगता है
      class _MissingId: ...
      
      MISSING_ID = _MissingId()
      
      # elsewhere  
      from ... import MISSING_ID  
      
      Symbols की याद आती है
    • PEP में कहा गया है कि अगर आप कई संबंधित sentinel values परिभाषित करना चाहते हैं, या उनके बीच क्रमबद्धता भी देना चाहते हैं, तो उसकी जगह Enum या कुछ वैसा उपयोग करें
  • मुझे लगता है, सीधे JavaScript का Symbol API लाना बेहतर होता। वह सामान्य रूप से भी उपयोगी है, और यहाँ जिस समस्या को हल करने की कोशिश की जा रही है, उसे भी हल कर देता है