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/