[Python-ideas] A better (simpler) approach to PEP 505

David Mertz mertz at gnosis.cx
Mon Jul 23 11:12:45 EDT 2018


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.

Here is a way of solving the "deep attribute access to messy data" problem
that is:

(1) Much more explicit
(2) Requires no change in syntax
(3) Will not be a bug magnet
(4) Inasmuch as there are semantic traps, they are announced by the use of
a class whose documentation would be pointed to for readers

The API that could be useful might be something like this:

In [1]: from none_aware import NoneAware
In [2]: from types import SimpleNamespace
In [3]: foo = SimpleNamespace()
In [4]: foo.bar = SimpleNamespace()
In [5]: foo.bar.baz = SimpleNamespace()
In [6]: foo.bar.baz.blat = 42
In [7]: NoneAware(foo).bar.blim
Out[7]: <none_aware.NoneAware at 0x11156a748>
In [8]: NoneAware(foo).bar.blim.unbox()
In [9]: NoneAware(foo).bar.baz.blat.unbox()
Out[9]: 42
In [10]: NoneAware(foo).bar.baz.blat
Out[10]: <none_aware.NoneAware at 0x11157d908>
In [11]: NoneAware(foo).bar.baz.flam.unbox()
In [12]: NoneAware(foo).bar.baz.flam
Out[12]: <none_aware.NoneAware at 0x1115832b0>


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.

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.

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:

favorite = cfg?.user?.profile?.food ?? "Spam"


(i.e. a configuration may be incomplete at any level, if levels are missing
default favorite food is Spam).  We could simply spell that:

favorite = NoneAware(cfg, "Spam").user.profile.food.unbox()


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.

So completely toy implementation:

class NoneAware(object):
    def __init__(self, thing, sentinal=None):
        self.thing = thing
        self.sentinal = sentinal

    def __getattr__(self, attr):
        try:
            return NoneAware(getattr(self.thing, attr))
        except AttributeError:
            return NoneAware(self.sentinal)

    def unbox(self):
        return self.thing

-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180723/bc2a74ab/attachment-0001.html>


More information about the Python-ideas mailing list