<div dir="ltr"><div>The need addressed by PEP 505 is real; it's also MUCH more niche and uncommon than something that would merit new syntax.  Moreover, the actual legitimate purpose served by the PEP 505 syntax is easily served by existing Python simply by using a wrapper class.</div><div><br></div><div>Here is a way of solving the "deep attribute access to messy data" problem that is:</div><div><br></div><div>(1) Much more explicit</div><div>(2) Requires no change in syntax</div><div>(3) Will not be a bug magnet</div><div>(4) Inasmuch as there are semantic traps, they are announced by the use of a class whose documentation would be pointed to for readers</div><div><br></div><div>The API that could be useful might be something like this:</div><div><br></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><div><font face="monospace, monospace">In [1]: from none_aware import NoneAware</font></div></div><div><div><font face="monospace, monospace">In [2]: from types import SimpleNamespace</font></div></div><div><div><font face="monospace, monospace">In [3]: foo = SimpleNamespace()</font></div></div><div><div><font face="monospace, monospace">In [4]: foo.bar = SimpleNamespace()</font></div></div><div><div><font face="monospace, monospace">In [5]: foo.bar.baz = SimpleNamespace()</font></div></div><div><div><font face="monospace, monospace">In [6]: foo.bar.baz.blat = 42</font></div></div><div><div><font face="monospace, monospace">In [7]: NoneAware(foo).bar.blim</font></div></div><div><div><font face="monospace, monospace">Out[7]: <none_aware.NoneAware at 0x11156a748></font></div></div><div><div><font face="monospace, monospace">In [8]: NoneAware(foo).bar.blim.unbox()</font></div></div><div><div><font face="monospace, monospace">In [9]: NoneAware(foo).bar.baz.blat.unbox()</font></div></div><div><div><font face="monospace, monospace">Out[9]: 42</font></div></div><div><div><font face="monospace, monospace">In [10]: NoneAware(foo).bar.baz.blat</font></div></div><div><div><font face="monospace, monospace">Out[10]: <none_aware.NoneAware at 0x11157d908></font></div></div><div><div><font face="monospace, monospace">In [11]: NoneAware(foo).bar.baz.flam.unbox()</font></div></div><div><div><font face="monospace, monospace">In [12]: NoneAware(foo).bar.baz.flam</font></div></div><div><div><font face="monospace, monospace">Out[12]: <none_aware.NoneAware at 0x1115832b0></font></div></div></blockquote><br clear="all"><div>The particular names I use are nothing special, and better ones might be found.  I just called the class NoneAware and the "escape" method `.unbox()` because that seemed intuitive at first brush.</div><div><br></div><div>I don't disagree that needing to call .unbox() at the end of the chained attribute access is a little bit ugly.  But it's a lot less ugly than large family of new operators.  And honestly, it's a nice way of being explicit about the fact that we're entering then leaving a special world where attribute accesses don't fail.</div><div><br></div><div>I haven't implemented the equivalent dictionary lookups in the below.  That would be straightforward, and I'm sure my 5 minute throwaway code could be improved in other ways also.  But something better than this in the standard library would address ALL the actual needs described in PEP 505.  Even the pattern Steve Dower is especially fond of like:</div><div><br></div><div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><span style="font-family:monospace,monospace">favorite = cfg?.user?.profile?.food ?? "Spam"</span><br></div></blockquote></div><div><br></div><div>(i.e. a configuration may be incomplete at any level, if levels are missing default favorite food is Spam).  We could simply spell that:</div><div><br></div><div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><font face="monospace, monospace">favorite = NoneAware(cfg, "Spam").user.profile.food.unbox()</font></div></blockquote></div><div><br></div><div>I think that's 14 characters more in this example, but still compact.  We could get that down to 2 characters if we used one-letter names for the class and method.  I suppose down to zero characters if .unbox() was a property.</div><div><br></div><div>So completely toy implementation:</div><div><br></div><div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><div><font face="monospace, monospace">class NoneAware(object):</font></div><div><font face="monospace, monospace">    def __init__(self, thing, sentinal=None):</font></div><div><font face="monospace, monospace">        self.thing = thing</font></div><div><font face="monospace, monospace">        self.sentinal = sentinal</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    def __getattr__(self, attr):</font></div><div><font face="monospace, monospace">        try:</font></div><div><font face="monospace, monospace">            return NoneAware(getattr(self.thing, attr))</font></div><div><font face="monospace, monospace">        except AttributeError:</font></div><div><font face="monospace, monospace">            return NoneAware(self.sentinal)</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    def unbox(self):</font></div><div><font face="monospace, monospace">        return self.thing</font></div></div><div><br></div></blockquote></div>-- <br><div dir="ltr" class="gmail_signature">Keeping medicines from the bloodstreams of the sick; food <br>from the bellies of the hungry; books from the hands of the <br>uneducated; technology from the underdeveloped; and putting <br>advocates of freedom in prisons.  Intellectual property is<br>to the 21st century what the slave trade was to the 16th.<br></div></div>