PEP 661 – Sentinel değerleri, 5 yıl sonra onaylandı
(peps.python.org)- PEP 661,
Nonegeçerli bir değer olduğunda ondan ayrı biçimde ayırt edilebilen sentinel değerleri oluşturmak için Python yerleşik çağrılabilir nesnesisentinel()ile C APIPySentinel_New()işlevini önerir - Mevcut
_sentinel = object()deyimi, fonksiyon imzalarındareprçıktısının uzun ve belirsiz olmasına yol açar; ayrıca açık tip imzası, kopyalama ve pickle işlemlerinde sorun çıkarabilir sentinel('MISSING')çağrısı, kısa birrepre sahip yeni ve benzersiz bir nesne oluşturur; aynı sentinelin paylaşılması gerekiyorsaMISSING = sentinel('MISSING')örneğindeki gibi bir değişkene atanıp açıkça yeniden kullanılmalıdır- Sentinel değerlerinin
isile karşılaştırılması önerilir ve bunlar truthy olarak değerlendirilir;copy.copy()ilecopy.deepcopy()aynı nesneyi döndürür ve bir modülden adıyla import edilebildiğinde pickle sonrası da kimliğini korur - Tip sistemi,
int | MISSINGgibi ifadelerde sentinelin kendisinin tip ifadesi olarak kullanılmasına izin verir; en güncel resmi belge ise Python 3.15’teki [sentinel](<https://docs.python.org/3.15/library/functions.html#sentinel "(in Python v3.15>)") dokümantasyonudur
Ortaya çıkış arka planı
- Benzersiz bir yer tutucu olan sentinel değeri (sentinel value), fonksiyon argümanı verilmediğinde kullanılacak varsayılan değer, arama başarısızlığını gösteren dönüş değeri veya eksik veriyi gösteren değer gibi amaçlarla kullanılır
- Python’da bu tür kullanım için genellikle özel değer
Nonebulunur; ancakNone’ın kendisinin de geçerli bir değer olduğu bağlamlarda,Nonedan ayrılan ayrı bir sentinel değerine ihtiyaç duyulur - Mayıs 2021’de python-dev posta listesinde,
traceback.print_exceptioniçinde kullanılan sentinel değerini daha iyi uygulamanın yolları tartışıldı - Mevcut uygulama, yaygın bir deyim olan
_sentinel = object()yaklaşımını kullanıyordu; ancakreprçıktısı aşırı uzun ve yetersiz bilgi verdiği için fonksiyon imzasını okumayı zorlaştırıyordu>>> 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) - Tartışma sırasında mevcut sentinel uygulamalarındaki başka sorunlar da ortaya çıktı
- Bazı sentinellerin kendine özgü bir tipi yoktur; bu yüzden sentinelin varsayılan değer olarak kullanıldığı fonksiyonlar için açık tip imzası tanımlamak zordur
- Kopyalama sonrası ayrı bir örnek oluştuğu için
iskarşılaştırması başarısız olabilir ve beklenmedik davranışlar görülebilir - Bazı yaygın deyimlerde, pickle sonrası unpickle edildiğinde de benzer sorunlar yaşanır
- Victor Stinner, Python standart kütüphanesinde kullanılan sentinel değerlerinin bir listesini sundu; bunun sonucunda standart kütüphanenin içinde bile farklı uygulama yöntemlerinin kullanıldığı ve birçoğunun yukarıdaki sorunlardan en az birine sahip olduğu görüldü
- discuss.python.org üzerindeki oylama, 39 oyla net bir sonuca ulaşamadı
- %40, “mevcut durum yeterince iyi ve tutarlılık gerekli değil” seçeneğini işaretledi
- Çoğunluk, bir veya daha fazla standartlaştırılmış çözümü seçti
- %37, “yeni bir özel sentinel factory/class/metaclass yapısının tutarlı biçimde kullanılması ve standart kütüphanede açıkça sunulması” seçeneğini tercih etti
- Bu karışık sonuçlar nedeniyle PEP yazıldı ve basit, iyi bir standart kütüphane uygulamasının hem standart kütüphane içinde hem de dışında yararlı olacağı sonucuna varıldı
- Standart kütüphanedeki mevcut tüm sentinellerin bu yöntemle değiştirilmesi zorunlu değildir; karar ilgili bakımcıların takdirine bırakılmıştır
- PEP belgesi tarihsel bir belgedir; en güncel resmi belge Python 3.15’teki [
sentinel](<https://docs.python.org/3.15/library/functions.html#sentinel "(in Python v3.15>)") dokümantasyonudur
Tasarım ölçütleri
- Sentinel nesnesi,
isişleciyle karşılaştırıldığında her zaman kendisiyle aynı, diğer tüm nesnelerden farklı olmalıdır - Sentinel nesnesi oluşturma işlemi basit ve sezgisel bir tek satırlık kod olmalıdır
- Gerektiği kadar çok farklı sentinel değer kolayca tanımlanabilmelidir
- Sentinel nesnesi kısa ve açık bir
repre sahip olmalıdır - Sentinel için açık bir tip imzası kullanılabilmelidir
- Kopyalama sonrasında da doğru çalışmalı, pickle ve unpickle sırasında öngörülebilir davranış sergilemelidir
- CPython 3.x ve PyPy3 üzerinde çalışmalı, mümkünse diğer Python uygulamalarında da çalışmalıdır
- Hem uygulama hem kullanım olabildiğince basit ve sezgisel olmalı; Python öğrenirken ek bir özel kavram yükü oluşturmamalıdır
- Standart kütüphane
sentinelsveyasentinelgibi PyPI paketlerinin uygulamalarına bağımlı olamayacağından, standart kütüphane içinde kullanılabilecek bir uygulamaya ihtiyaç vardır
sentinel() özellikleri
- Yeni yerleşik çağrılabilir nesne
sentineleklendi>>> MISSING = sentinel('MISSING') >>> MISSING MISSING sentinel()yalnızca konumsal olan tek birnameargümanı alır venamemutlakastrolmalıdır- Dize olmayan bir değer verilirse
TypeErroroluşur name, sentinel'in adı vereprdeğeri olarak kullanılır- Sentinel nesnesinin iki herkese açık niteliği vardır
__name__: sentinel adı__module__:sentinel()çağrısının yapıldığı modül adı
sentinelalt sınıflanamazsentinel(name)her çağrıldığında yeni bir sentinel nesnesi döndürür- Aynı sentinel birden fazla yerde kullanılacaksa, mevcut
MISSING = object()kalıbında olduğu gibi bir değişkene atanmalı ve aynı nesne açıkça yeniden kullanılmalıdırMISSING = sentinel('MISSING') def read_value(default=MISSING): ... - Belirli bir değerin sentinel olup olmadığını denetlerken,
Noneiçin olduğu gibiisoperatörünün kullanılması önerilir ==karşılaştırması da yalnızca kendisiyle karşılaştırıldığındaTruedöndürecek şekilde beklendiği gibi çalışırif value is MISSING:gibi kimlik denetimleri, genellikleif value:veyaif not value:gibi boolean denetimlerinden daha uygundur- Sentinel nesneleri truthy'dir; yani boolean değerlendirmesinin sonucu
Trueolur- Bu, rastgele bir sınıfın varsayılan davranışıyla ve
Ellipsis'in boolean değeriyle aynıdır - Falsy olan
None'dan farklıdır
- Bu, rastgele bir sınıfın varsayılan davranışıyla ve
- Bir sentinel nesnesi
copy.copy()veyacopy.deepcopy()ile kopyalanırsa aynı nesne döndürülür - Tanımlandığı modülden adıyla içe aktarılabilen sentineller, standart pickle mekanizmasına göre pickle ve unpickle sonrasında da kimliğini korur
MISSING = sentinel('MISSING') assert pickle.loads(pickle.dumps(MISSING)) is MISSING sentinel(), sentinel oluşturulurken çağrıldığı modülü__module__niteliğine kaydeder- Pickle işlemi sentinel'i modül ve ad ile kaydeder; unpickle ise modülü içe aktardıktan sonra sentinel'i adıyla alır
- Modül ve ad üzerinden içe aktarılamayan sentineller, örneğin yerel kapsamda oluşturulup modül geneline veya sınıf niteliğindeki eşleşen bir ada atanmamış sentineller, pickle edilemez
- Sentinel nesnesinin
reprdeğeri,sentinel()fonksiyonuna verilennameolur; örtük bir modül niteleyicisi eklenmez - Nitelikli bir
reprgerekiyorsa bu, ada açıkça eklenmelidir>>> MyClass_NotGiven = sentinel('MyClass.NotGiven') >>> MyClass_NotGiven MyClass.NotGiven - Sentinel nesnelerinin sıralama karşılaştırmaları tanımlı değildir
- Sentinel'ler weakref desteği sunmaz
Tip belirtimi
- Tip belirtilmiş Python kodunda sentinel kullanımını açık ve basit hale getirmek için, tip sistemine sentinel nesneleri için özel işlem eklenir
- Sentinel nesneleri, tip ifadeleri içinde kendilerini temsil eden bir değer olarak kullanılabilir
- Bu, mevcut tip sisteminde
None'ın ele alınış biçimine benzerdirMISSING = sentinel('MISSING') def foo(value: int | MISSING = MISSING) -> int: ... - Tip denetleyicileri,
NAME = sentinel('NAME')biçimindeki sentinel oluşturmayı yeni bir sentinel nesnesi oluşturma olarak tanımalıdır sentinel()fonksiyonuna verilen ad, atamanın sol tarafındaki adla eşleşmiyorsa tip denetleyicisi hata vermelidir- Bu söz dizimiyle tanımlanan sentineller tip ifadelerinde kullanılabilir
- İlgili sentinel tipi, üye olarak yalnızca sentinel nesnesinin kendisini içeren bir tam statik tipi temsil eder
- Tip denetleyicileri,
isveis notoperatörlerini kullanarak sentinel içeren union tiplerini daraltmayı desteklemelidirfrom 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) - Çalışma zamanı uygulaması, tip ifadelerinde kullanımı desteklemek için
__or__ve__ror__metotlarına sahip olmalı; bu metotlartyping.Unionnesnesi döndürmelidir - Typing Council, bu önerinin tiple ilgili kısmını destekliyor
C API
- Sentinel'ler C uzantılarında da yararlı olabileceğinden, iki yeni C API fonksiyonu öneriliyor
PyObject *PySentinel_New(const char *name, const char *module_name)yeni bir sentinel nesnesi oluştururbool PySentinel_Check(PyObject *obj)bir nesnenin sentinel olup olmadığını denetler- C kodu, belirli bir sentinel olup olmadığını denetlerken
==operatörünü kullanabilir
Uyumluluk ve güvenlik
- Yeni bir yerleşik ad eklendiğinde, çıplak
sentineladının şu andaNameErrorverdiğini varsayan kod artık aynı sonucu görmez - Bu, yeni yerleşik adlar eklenirken genel olarak ortaya çıkan bir uyumluluk değerlendirmesidir
- Zaten var olan yerel, global veya içe aktarılmış
sentineladları etkilenmez - Halihazırda
sentineladını kullanan kodların yeni yerleşik nesneyi kullanacak şekilde uyarlanması gerekebilir ve yerleşik adlarla çakışmaları uyaran linter'lar yeni uyarılar verebilir - Yeni yerleşik işlevsellik için genel dokümantasyon yöntemleri olan docstring'ler, kütüphane belgeleri ve “What’s New” bölümü yeterli kabul edilir
- Bu önerinin güvenlik açısından bir etkisi olmadığı değerlendiriliyor
Referans uygulama ve backport
- Referans uygulama, CPython pull request'i [10] ile sunuluyor
- Önceki referans uygulama, ayrı bir GitHub deposunda [7] yer alıyor
- Amaçlanan davranışın taslağı şu şekilde
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 modülünde bir backport bulunuyor, ancak bu şu anda PEP'in yinelemeli sürümünün davranışıyla tam olarak eşleşmiyor
Reddedilen alternatifler
-
NotGiven = object()kullanımı- Bu yaklaşım, PEP’in tasarım ölçütlerinde ele alınan tüm dezavantajlara sahiptir
repruzun ve net değildir, tip imzasını açık hale getirmek zordur ve kopyalama veya pickling ile ilgili sorunlar ortaya çıkabilir
-
MISSINGveyaSentinelgibi tek bir yeni sentinel değeri eklemek- Tek bir değer birden çok yerde birden çok amaçla kullanılırsa, bazı kullanım senaryolarında bu değerin kendisinin geçerli bir değer olmayacağından her zaman emin olmak zordur
- Amaca özel, birbirinden farklı sentinel değerleri, olası edge case’ler hesaba katılmadan daha güvenle kullanılabilir
- Sentinel değerleri, kullanım bağlamına uygun anlamlı bir ad ve
reprsağlayabilmelidir - Bu seçenek oylamada yalnızca %12 seçilerek çok düşük popülerlikte kaldı
-
Mevcut
Ellipsissentinel değerini kullanmakEllipsisbaşlangıçta böyle bir amaç için tasarlanmış bir değer değildirpassyerine boş sınıf veya fonksiyon blokları tanımlamak için daha sık kullanılmaya başlansa da, amaca özel farklı sentinel değerleri kadar her durumda güvenle kullanılamaz
-
Tek değerli
Enumkullanmak- Önerilen deyim şu şekildedir
class NotGivenType(Enum): NotGiven = 'NotGiven' NotGiven = NotGivenType.NotGiven - Tekrar gereksiz derecede fazladır ve
repr,<NotGivenType.NotGiven: 'NotGiven'>gibi aşırı uzundur - Daha kısa bir
reprtanımlanabilir, ancak bu da kodu ve tekrarı daha da artırır - Oylamadaki 9 seçenek arasında oy alamayan tek seçenekti ve en az popüler olanıydı
-
Sentinel sınıfı dekoratörü
- Önerilen deyim şu şekildedir
@sentinel class NotGivenType: pass NotGiven = NotGivenType() - Dekoratörün uygulanması kendi başına basit ve açık olabilir, ancak deyim fazla ayrıntılı, tekrarlı ve hatırlaması zordur
- Önerilen deyim şu şekildedir
-
Sınıf nesnesi kullanmak
- Sınıflar özünde singleton olduğundan, sentinel değer olarak kullanılmaları fikri mümkündür
- En basit biçim şu şekildedir
class NotGiven: pass- Net bir
reprelde etmek için bir metaclass veya sınıf dekoratörü gerekir
class NotGiven(metaclass=SentinelMeta): pass@Sentinel class NotGiven: pass - Net bir
- Sınıfları bu şekilde kullanmak alışılmadık olduğundan kafa karıştırıcı olabilir
- Yorum olmadan kodun niyetini anlamak zordur ve sentinel’in callable hale gelmesi gibi beklenmedik, istenmeyen davranışlar ortaya çıkar
-
Uygulama olmadan yalnızca önerilen standart deyimi tanımlamak
- Yaygın mevcut deyimlerin çoğu önemli dezavantajlara sahiptir
- Şimdiye kadar bu dezavantajlardan kaçınırken açık ve kısa bir deyim bulunamamıştır
- İlgili oylamada deyim önerisi seçeneği düşük popülerlikte kaldı ve en çok oy alan seçenek bile %25’te kaldı
-
Yeni bir standart kütüphane modülü kullanmak
- İlk taslak, yeni bir
sentinelsveyasentinellibmodülüneSentinelsınıfı eklenmesini öneriyordu - Tek bir herkese açık callable nesne için yeni bir modül eklemek gereksizdir
- Modül kullanımı, mevcut
object()deyimine göre özelliğin kullanımını daha zahmetli hale getirir - Steering Council de bunun
object()kadar kolay kullanılabilmesi için özellikle built-in bir özellik olmasını tavsiye etti sentinelsadı, zaten aktif olarak kullanılan bir PyPI paketiyle çakışır; bunu built-in yapmak ad sorununu önler
- İlk taslak, yeni bir
-
Modül bazlı sentinel ad kayıt defteri kullanmak
- İlk taslak, sentinel adlarının modül içinde benzersiz olmasını önermişti
- Bu tasarımda, aynı modülde
sentinel("MISSING")tekrar tekrar çağrıldığında, modül adı ve sentinel adını anahtar olarak kullanan süreç genelindeki bir kayıt defteri üzerinden aynı nesne döndürülür - Bu davranış fazla örtük bulunduğu için reddedildi
- Paylaşılan bir sentinel gerekiyorsa, mevcut
MISSING = object()örneğinde olduğu gibi bir tane açıkça tanımlanıp adıyla yeniden kullanılabilir - Yerel scope’ta, her çağrıda veya her tekrarda yeni bir sentinel istenebilir; bu nedenle
sentinel(name)tekrar çağrıları,object()tekrar çağrıları gibi birbirinden farklı nesneler üretmelidir - Kayıt defteri kaldırıldığında uygulama ve zihinsel model daha basit hale gelir ve geriye yalnızca
sentinel(name)ifadesininreprdeğerinameolan yeni, benzersiz bir nesne oluşturduğu kuralı kalır
-
Modül adını otomatik keşfetmek veya iletmek
- İlk taslak, kayıt defteri tabanlı tasarımı desteklemek için isteğe bağlı bir
module_nameargümanı öneriyordu - Kayıt defteri kaldırılınca, herkese açık
module_nameargümanı artık temel öneri için gerekli değildir - Uygulama,
TypeVarbenzeri şekilde, pickle’ın import edilebilir sentinel’leri modül ve ad üzerinden serileştirebilmesi için çağıran modülü dahili olarak kaydeder - Dahili modül adı, sentinel’in
reprdeğerini etkilemez - Modül adı veya sınıf adını içeren bir
repristenirse,sentinel("mymodule.MISSING")örneğinde olduğu gibi teknameargümanı içinde açıkça belirtilebilir
- İlk taslak, kayıt defteri tabanlı tasarımı desteklemek için isteğe bağlı bir
-
reprözelleştirmesine izin vermek- Bunun bir avantajı, mevcut sentinel değerlerin
reprdeğişmeden bu yaklaşıma taşınabilmesiydi - Ancak ek karmaşıklığa değmediği düşünülerek dışarıda bırakıldı
- Bunun bir avantajı, mevcut sentinel değerlerin
-
Boolean değerlendirmesinin özelleştirilmesine izin vermek
- Tartışmalarda, sentinel’in açıkça truthy, falsy veya
booldönüşümüne kapalı hale getirilebilmesi seçeneği ele alındı - Bazı üçüncü taraf sentinel’ler, falsy davranışı herkese açık API’nin bir parçası olarak sunar
- Birçok katılımcı, boolean bağlamında istisna fırlatmanın kimlik denetimini daha iyi zorladığını düşündü
- PEP, sıradan nesnelerin varsayılan truthy davranışını koruyup kimlik denetimini önermesi bakımından ilk öneriyi daha basit tuttu
- Özelleştirilebilir boolean davranışı, ek API ve tür belirtme karmaşıklığına değdiği düşünülürse daha sonra değerlendirilebilir
- Tartışmalarda, sentinel’in açıkça truthy, falsy veya
-
Tip anotasyonlarında
typing.Literalkullanmak- Tartışmalarda birçok kişi bunu önerdi ve PEP de başlangıçta bu yaklaşımı benimsedi
- Ancak
Literal["MISSING"], sentinel değeriMISSINGiçin ileri referans değil, string değeri"MISSING"anlamına geldiğinden kafa karışıklığı yaratabilir - Çıplak ad kullanımı da tartışmalarda sıkça önerildi
- Çıplak ad yaklaşımı,
None’ın oluşturduğu emsali ve iyi bilinen deseni izler; import gerektirmez ve çok daha kısadır
Ek kullanım yönergeleri
- Sentinel’i sınıf scope’unda tanımlarken, ad çakışmasını önlemek isterken veya nitelikli bir
reprdaha açık olacaksa, istenen nitelikli adı açıkça iletmek gerekir>>> class MyClass: ... NotGiven = sentinel('MyClass.NotGiven') >>> MyClass.NotGiven MyClass.NotGiven - Fonksiyon veya metot içinde sentinel oluşturulmasına izin verilir
- Her
sentinel()çağrısı farklı bir nesne oluşturduğundan, yerel scope’ta oluşturulan sentinel’ler o scope içindeobject()çağrısıyla oluşturulan değerler gibi davranır NotImplemented’ın boolean değeriTrue’dur, ancak Python 3.9’dan beri bunun kullanımı kullanımdan kaldırılma sürecindedir ve deprecation warning üretir- Bu kullanımdan kaldırma, bpo-35712 [8] içinde açıklanan
NotImplemented’a özgü sorunlardan kaynaklanır - Birden fazla ilişkili sentinel değeri tanımlamanız veya bunlar arasında bir sıralama tanımlamanız gerekiyorsa,
Enumya da benzeri bir yaklaşım kullanılmalıdır - Bu tür sentinel’lerin type annotation’ı hakkında, typing-sig posta listesinde [9] çeşitli seçenekler tartışılmıştır
1 yorum
Lobste.rs görüşleri
Seçilen adın anlam olarak fazla dar olması tuhaf hissettiriyor
Sadece isme bakınca benzersiz sembol gibi bir şeyin daha esnek bir temel yapı taşı olması beklenirdi. Pratikte neredeyse sembol gibi davranacakları için o şekilde kullanılabilirler, ama buna “Sentinels” denmesi kulağa garip geliyor. Belki de Lisp’e alışık olduğum için böyle hissediyorum
SENTINEL_AileSENTINEL_B'nin farklı tipler olmasını sağlamak gibi görünüyor; böylece bir değerinis_a SENTINEL_Aolup olmadığı sorulabiliyorRuby sembolleri böyle çalışmaz:
:beef.is_a? :droog.class #=> trueLiteralve literal string’ler varBunlara adlandırılmış sentinel denmesinin nedeni, sentinel values'ın Python’da yaygın bir kavram ve kalıp olması ve sentinel’lerin bu kalıbın kullanımından doğan bazı sorunları dar kapsamda çözmeye çalışması. “Motivation” ve “Rationale” bölümlerinde anlatıldığı gibi
Ayrıca sentinel’lerin değer semantiği yoktur; bu yüzden aynı adlı iki sentinel bile farklı değerlerdir ve birbirine eşit değildir. Dolayısıyla semboller gibi davranmazlar ve o şekilde kullanılmamalılar
Adlandırılmış argümanların varsayılan değeri sorununda, Typst’te
noneyanında sadeceautodeğeri eklenmesi bile istenen adlandırılmış argüman arayüzlerinin neredeyse tamamını ifade etmeye yeterYalnızca
none, çoğu adlandırılmış argüman varsayılanı için anlamsal olarak pek uygun düşmüyor.nonevarsayılan dönüş değeri olarak iyi olabilir, ama fonksiyon argümanı olduğunda çoğu zaman bir isim olarak doğru anlamı taşımıyor.matrix(axes=None)eksenleri kaldırmak mı demek, yoksa her zamanki gibi korumak mı, belirsiz.nonegeçirmekle hiçbir şey geçirmemek arasında fark olup olmadığı da net değil. Parametrenin verilmiş olup olmadığını ayırt etmek için çoklu dispatch’e gidilirse, o parametrenin davranışını belgelemek için merkezi bir yer de kayboluyorauto, “eldeki bilgiyle uygun olanı yap” anlamını doğrudan taşıyan harika bir varsayılan değer.auto | noneimzası daha açık bir boolean gibi kullanılabilir veT | auto | none, fonksiyonun değeri nasıl kullanacağı hakkında epey bilgi verir. ÖrneğinTbircolorise,automuhtemelen beyaz/siyah gibi bir varsayılan seçmek veya üst öğeden miras almak anlamına gelir;Trengi açıkça ayarlar;noneise bağlama göre ya rengi hiç ayarlamamak ya da saydam olarak ele almak anlamına gelebilirİlginç; bazı paketlerin semantiğinin nasıl değişeceğini merak ediyorum. Örneğin
Item | Nonedöndürmek yerine şöyle yazılabilirElbette birden fazla sentinel ile ek anlam da taşınabilir. Bu zaten mümkündü, ama belgelerde “resmen önerilen” bir yol yoktu. Bu, paket yazarlarını farklı bir yöne çekebilir
Biraz zorlama bir örnek ama bu durumda, mevcut bir ID olup bağlı bir değerin bulunmaması ile böyle bir ID’nin hiç olmaması nedeniyle başarısız olunması ayırt edilebilir. “Pythonic” yaklaşım muhtemelen istisna kullanmak olurdu, ama bu normalde Python yazarken görülenden daha çok işlevsel bir yaklaşım gibi duruyor
Bence doğrudan JavaScript’in
SymbolAPI’sini eklemek daha iyi olurdu. Genel kullanım açısından da faydalı olurdu ve burada çözülmeye çalışılan sorunu da birlikte çözerdi