PEP 661 – Sentinel मान, 5 साल बाद मंजूर
(peps.python.org)- PEP 661
Noneके मान्य value होने वाली स्थितियों में अलग से पहचाने जा सकने वाले sentinel value बनाने के लिए Python built-in callablesentinel()और C APIPySentinel_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 पढ़ना कठिन हो जाता था>>> help(traceback.print_exception) Help on function print_exception in module traceback: print_exception(exc, /, value=<object object at 0x000002825DF09650>, tb=<object object at 0x000002825DF09650>, 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
isoperator से तुलना करने पर हमेशा स्वयं के समान होना चाहिए और किसी भी दूसरे 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 argumentnameलेता है, और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की तरहisoperator का उपयोग करने की सिफारिश की जाती है ==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से अलग है
- यह arbitrary class के default behavior और
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 notoperators का उपयोग करके 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 होने चाहिए, और ये methodstyping.Unionobject लौटाते हैं - 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: """Unique sentinel values.""" __slots__ = ("__name__", "_module_name") def __init_subclass__(cls): raise TypeError("type 'sentinel' is not an acceptable base type") def __init__(self, name, /): if not isinstance(name, str): raise TypeError("sentinel name must be a string") 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% समर्थन मिला, इसलिए यह बहुत अलोकप्रिय था
-
मौजूदा
Ellipsissentinel मान का उपयोगEllipsisमूल रूप से इस उद्देश्य के लिए नहीं बनाया गया थाpassकी जगह खाली class या function block परिभाषित करने में इसका उपयोग बढ़ा है, लेकिन यह समर्पित अलग sentinel मानों जितना हर स्थिति में भरोसेमंद नहीं है
-
एकल-मान
Enumका उपयोग- प्रस्तावित idiom इस प्रकार है
class NotGivenType(Enum): NotGiven = 'NotGiven' NotGiven = NotGivenType.NotGiven - इसमें दोहराव बहुत अधिक है, और
repr<NotGivenType.NotGiven: 'NotGiven'>की तरह बहुत लंबा है - छोटा
reprपरिभाषित किया जा सकता है, लेकिन इससे code और दोहराव और बढ़ता है - वोटिंग के 9 विकल्पों में यह एकमात्र विकल्प था जिसे एक भी वोट नहीं मिला, इसलिए यह सबसे कम लोकप्रिय था
-
sentinel class decorator
- प्रस्तावित idiom इस प्रकार है
@sentinel class NotGivenType: pass NotGiven = NotGivenType() - decorator का implementation स्वयं सरल और स्पष्ट हो सकता है, लेकिन यह idiom बहुत लंबा, दोहरावपूर्ण और याद रखने में कठिन है
- प्रस्तावित 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याsentinellibmodule मेंSentinelclass जोड़ने का प्रस्ताव था - केवल एक public callable object के लिए नया module जोड़ना अनावश्यक है
- module उपयोग करने से functionality का इस्तेमाल मौजूदा
object()idiom की तुलना में कम सुविधाजनक हो जाता है - Steering Council ने भी विशेष रूप से सिफारिश की कि इसे
object()जितना आसान इस्तेमाल होने वाला built-in feature बनाया जाए sentinelsनाम पहले से सक्रिय रूप से उपयोग किए जा रहे PyPI package से टकराता है, और built-in feature बनाने से यह naming समस्या टल जाती है
- शुरुआती draft में नए
-
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_nameargument प्रस्तावित था - registry हटने के बाद public
module_nameargument की अब मुख्य प्रस्ताव में आवश्यकता नहीं रही - implementation,
TypeVarकी तरह, calling module को अंदरूनी रूप से रिकॉर्ड करता है ताकि pickling import किए जा सकने वाले sentinel को module और नाम के आधार पर serialize कर सके - अंदरूनी module नाम sentinel के
reprको प्रभावित नहीं करता - अगर module नाम या class नाम शामिल वाला
reprचाहिए, तोsentinel("mymodule.MISSING")की तरह उसे एकलnameargument में स्पष्ट रूप से शामिल किया जा सकता है
- शुरुआती draft में registry-आधारित design को support करने के लिए वैकल्पिक
-
reprcustomization की अनुमति- इसका लाभ यह था कि मौजूदा sentinel मानों को
reprबदले बिना इस तरीके में migrate किया जा सकता था - लेकिन अतिरिक्त जटिलता को उचित न मानते हुए इसे बाहर रखा गया
- इसका लाभ यह था कि मौजूदा sentinel मानों को
-
boolean evaluation customization की अनुमति
- चर्चा में sentinel को स्पष्ट रूप से truthy, falsy, या
boolconversion-असमर्थ बनाने का विकल्प विचाराधीन था - कुछ third-party sentinel अपने public API के हिस्से के रूप में falsy व्यवहार प्रदान करते हैं
- कई प्रतिभागियों ने माना कि boolean context में exception उठाना identity check को बेहतर ढंग से लागू कर सकता है
- PEP ने सामान्य object के default truthy व्यवहार को बनाए रखते हुए और identity check की सिफारिश करते हुए शुरुआती प्रस्ताव को सरल रखा
- custom boolean व्यवहार पर बाद में विचार किया जा सकता है, यदि यह तय हो कि इसके लिए अतिरिक्त API और typing जटिलता स्वीकार्य है
- चर्चा में sentinel को स्पष्ट रूप से truthy, falsy, या
-
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 को स्पष्ट रूप से पास करना चाहिए>>> class MyClass: ... NotGiven = sentinel('MyClass.NotGiven') >>> 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 टिप्पणियां
Lobste.rs की राय
चुना गया नाम मतलब के हिसाब से बहुत संकीर्ण लगता है, इसलिए अजीब लगता है
सिर्फ नाम देखें तो unique symbol जैसी कोई चीज़ ज़्यादा लचीला primitive लगती। व्यवहार में यह लगभग symbol की तरह काम करेगा, इसलिए उस तरह इस्तेमाल किया जा सकता है, लेकिन इसका नाम “Sentinels” रखना अटपटा है। शायद Lisp की आदत होने की वजह से ऐसा लग रहा हो
SENTINEL_A,SENTINEL_Bसे अलग type हो, ताकि पूछा जा सके कि कोई valueis_a SENTINEL_Aहै या नहींRuby के symbol ऐसे काम नहीं करते:
:beef.is_a? :droog.class #=> trueLiteralऔर 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के साथ सिर्फautovalue जोड़ देने से लगभग हर वांछित named-argument interface व्यक्त किया जा सकता हैसिर्फ
noneज़्यादातर named-argument defaults के लिए अर्थपूर्ण नहीं होता।nonedefault return value के रूप में तो अच्छा है, लेकिन function argument के रूप में यह कई बार संज्ञा की तरह सही मतलब नहीं देता।matrix(axes=None)का मतलब axes हटाना है, या उन्हें हमेशा की तरह बनाए रखना है, यह अस्पष्ट है।noneपास करना और कुछ भी पास न करना अलग है या नहीं, यह भी साफ़ नहीं है। अगर parameter की मौजूदगी को अलग दिखाने के लिए multiple dispatch की ओर जाएँ, तो उस parameter के व्यवहार को document करने की केंद्रीय जगह ही खो जाती हैautoएक बेहतरीन default value है, क्योंकि इसका सीधा अर्थ है “उपलब्ध जानकारी के आधार पर उचित तरीके से संभालो।”auto | nonesignature को ज़्यादा स्पष्ट boolean की तरह इस्तेमाल किया जा सकता है, औरT | auto | noneयह काफ़ी जानकारी देता है कि function value का कैसे उपयोग करेगा। उदाहरण के लिए, अगरTcolorहै, तोautoसंभवतः white/black जैसे default चुनेगा या parent से inherit करेगा,Tरंग को स्पष्ट रूप से सेट करेगा, औरnoneसंदर्भ के अनुसार या तो रंग बिल्कुल सेट नहीं करेगा या उसे transparent की तरह मानेगादिलचस्प है, और जिज्ञासा है कि कुछ packages की semantics कैसे बदलेंगी। उदाहरण के लिए,
Item | Noneलौटाने के बजाय नीचे जैसा लिखा जा सकता हैबेशक, कई sentinel के ज़रिए अतिरिक्त अर्थ भी जोड़े जा सकते हैं। पहले भी यह संभव था, लेकिन documentation में इसके लिए कोई “officially recommended” तरीका नहीं था। यह package authors को दूसरी दिशा में ले जा सकता है
उदाहरण थोड़ा बनावटी है, लेकिन इस मामले में यह अलग किया जा सकता है कि मौजूदा ID है पर उससे जुड़ी value नहीं है, या ऐसी ID ही मौजूद नहीं है इसलिए विफलता हुई। शायद “Pythonic” तरीका exception का उपयोग करना होगा, लेकिन यह आम Python लिखने की तुलना में ज़्यादा functional approach जैसा लगता है
मुझे लगता है, सीधे JavaScript का
SymbolAPI लाना बेहतर होता। वह सामान्य रूप से भी उपयोगी है, और यहाँ जिस समस्या को हल करने की कोशिश की जा रही है, उसे भी हल कर देता है