[Python-ideas] Delayed Execution via Keyword
Steven D'Aprano
steve at pearwood.info
Fri Feb 17 20:23:46 EST 2017
On Fri, Feb 17, 2017 at 06:06:26PM -0500, Joseph Hackman wrote:
[...]
> I think it would be key, like async/await, to narrowly define the scope in
> which the word delayed functions as a keyword.
The PEP makes it clear that's just a transition phase: they will be
turned into proper keywords in Python 3.7.
https://www.python.org/dev/peps/pep-0492/#id80
Python has had "pseudo-keywords" in the past, like "as":
[steve at ando ~]$ python2.5 -c "import math as as; print as"
<module 'math' from '/usr/local/lib/python2.5/lib-dynload/math.so'>
and it is my understanding that the core developers dislike this sort of
thing. As do I. You shouldn't count as getting the same special treament
as async/await. Maybe you will, maybe you won't.
> > A new keyword means it can't be back-ported to older versions, and will
> > break code.
> >
> >
> async and await both work fine, for the reasons listed above.
You're missing the point: code that uses async and await, whether as
pseduo-keywords or actual keywords, cannot easily be backported to
Python 3.4 or older.
If Python introduces a new built-in, say Aardvark, then it can be
back-ported:
try:
Aardvark
except NameError:
from backport import Aardvark
No such thing is possible for new syntax. So that counts as a
disadvantage of new syntax. Are we positive that there *must* be new
syntax to solve this problem?
(I think probably so, but it still counts as a disadvantage: that means
that the usefulness is reduced.)
> > > Unlike 'lambda' which returns a function (so the receiver must be
> > > lambda-aware), delayed execution blocks are for all purposes values. The
> > > first time the value (rather than location) is read,
> >
> > What counts as "reading" a value? Based on your example below, I can't
> > tell if passing the object to *any* function is enough to trigger
> > evaluation, or specifically print is the magic that makes it happen.
>
> So far I'm going with pretty much anything that isn't being the right-hand
> of an assignment. So coercion to different types, hashing (for use as a key
> in a dict or set), __repr__, etc would all be covered, as well as identity
> and comparisons. i.e.:
[...]
That will make it pretty much impossible to tell whether something is a
delayed "thunk" or not, since *any* attempt to inspect it in any way
will cause it to reify.
Maybe that's what we want.
> > That's easily done by having the "delayed" keyword cache each expression
> > it sees, but that seems like a bad idea to me:
> >
> > spam = delayed: get_random_string()
> > eggs = delayed: get_random_string() # the same expression
> >
> > spam.upper() # convert to a real value
> >
> > assert spam == eggs # always true, as they are the same expression
> >
>
> Since spam and eggs are two different instances of delayed expression, each
> one would be evaluated separately when they are read from (as operands for
> the equals operator). So no, even without the spam.upper(), they would not
> match.
Earlier we talked about delayed *expressions* always generating the same
value, now you're talking about *instances* rather than expressions. It
makes sense to have keep the standard Python object semantics, rather
than have the value of a delayed thunk cached by the textual expression
that generated it.
> > I think it is better to stick to a more straight-forward, easily
> > understood and debugged system based on object identity rather than
> > expressions.
> >
> >
> The caching means that:
> spam = delayed: calculate(1)
> eggs = spam
>
> eggs == spam would be true, and calculate would have only been called once,
> not twice.
That's not caching, that's simple identity. That's how assignment works
in Python, delayed calculation or not.
--
Steve
More information about the Python-ideas
mailing list