Le 23/07/2018 à 17:12, David Mertz a écrit :
> 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>
This has existed as libs for a while:

https://github.com/ekampf/pymaybe


It's interest is much more limited than in Haskell because in Python,
calls are not lazy, objects are dynamic and function calls are expensive.


Take the following:

foo().bar[0]

Doing it safely would be:

    val = foo()

    try:
        val = val.bar
    except AttributeError:
        val = None
    else:
        try:
            val = val[0]
        except KeyError:
            val = None

Clearly we see that the operation goes up to the end if everything goes
right. Otherwise, it jumps to the default value.

With the operator proposal:

    val = foo()?.bar?[0]

The "?" operator is like "or" / "and", and it will shotcircuit as well.

With the try / else proposal:

    val = try foo().bar[0] else None

This is supposed to transform this ast to into the first try/except
form. So it shortcircuit as well.

But with your proposal, it becomes:

    val = maybe(foo()).bar[0].or_else(None)

Which means

    foo() # call

    maybe(foo()) # call

    maybe(foo()).__getattr__('bar') # call

    maybe(foo()).__getattr__('bar').__getitem__(0) # call

    maybe(foo()).__getattr__('bar').__getitem__(0).or_else(None) # call

There is no shortcircuit, you get 5 calls in all cases, plus the maybe
object proxing the call to the underlying value every time. So that's 7
calls, added to the try/excepts you still have behind the scene.

Plus, you don't get the same calls depending of the values, but the
switch is implicit and behind the maybe object: no linter can save you
from a typo.

So yes, it works right now. But it's a suboptimal solution.
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to