David, can you elaborate on your example? if we replaced line four with
>>> x = my_lazy_func(b, delayed c) what would the value of `x` be, and how would this differ from either >>> x = delayed my_lazy_func(b, delayed c) or >>> x = delayed my_lazy_func(b, c) To put it another way, why does my_lazy_func being called not evaluate c, or are you implying that it too is some kind of special delayed function that needs to be explicitly computed, like in dask? --Josh On Fri, Feb 17, 2017 at 1:23 AM David Mertz <me...@gnosis.cx> wrote: > Dask also has a function delayed() that may be used as an decorator and in > other ways like: > > >>> from dask import delayed > > >>> from operator import add, mul > >>> a = delayed(add)(1, 2) > >>> b = delayed(mul)(a, 3) > > >>> b > Delayed('mul-1907f29b-60a4-48af-ba2a-938556555f9b') > > >>> c = b.compute() > > >>> c > 9 > > >>> b.dask > {'add-d49ba000-dd5d-4031-8c37-6514626a3d81': (<function _operator.add>, 1, > 2), > 'mul-1907f29b-60a4-48af-ba2a-938556555f9b': (<function _operator.mul>, > 'add-d49ba000-dd5d-4031-8c37-6514626a3d81', > 3)} > > > You *can* do pretty much anything you'd want to using this approach... > including the real job of Dask, to identify latent parallelism and execute > computations on many cores or many machines in a cluster. > > But actual syntax to do all of this would be really elegant. I think for > this to be as useful as I'd want, you'd sometimes need to be to continue > delaying computation rather than doing so on every access I guess as > syntax, the `delayed:` construct would work (I think having no colon would > more closely model `yield` and `yield from` and `await` and `async` which > this is kinda-sorta akin to). > > So for example, in a hypothetical Python 3.7+: > > >>> a = delayed 1 + 2 > > >>> b = delayed b * 3 > > >>> c = delayed 12/3 > > >>> my_lazy_func(b, delayed c) # evaluates b but not yet c > >>> b > 9 > >>> delayed c > <delayed object at 0x123456789> > > > If you want to do something like Dask... or for Dask itself to be able to > use it in some eventual version, you'd need to be able to keep objects from > evaluating even while you passed them around. The obvious use is for > finding parallelism, but other things like passing callbacks might find > this useful too. > > Dask delayed objects stay lazy until you explicitly `.compute()` on them > (which does so recursively on every lazy object that might go into the > computation). This hypothetical new keyword would have object evaluate > eagerly *unless* you explicitly kept them lazy. But the idea is that the > programmer would still get to decide in their code. > > > On Feb 16, 2017 9:53 PM, "Joseph Jevnik" <joe...@gmail.com> wrote: > > You might be interested in https://github.com/llllllllll/lazy_python, > which implements the features you describe but instead of a keyword it uses > a decorator. > > On Fri, Feb 17, 2017 at 12:24 AM, Joseph Hackman <josephhack...@gmail.com> > wrote: > > Howdy All! > > This suggestion is inspired by the question on "Efficient debug logging". > > > I propose a keyword to mark an expression for delayed/lazy execution, for > the purposes of standardizing such behavior across the language. > > The proposed format is: > delayed: <expr> > i.e. log.info("info is %s", delayed: expensiveFunction()) > > 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, or any method on the > delayed object is called, the expression is executed and the delayed > expression is replaced with the result. (Thus, the delayed expression is > only every evaluated once). > > Ideally: > a = delayed: 1+2 > b = a > print(a) #adds 1 and 2, prints 3 > # a and b are now both just 3 > print(b) #just prints 3 > > Mechanically, this would be similar to the following: > > class Delayed(): > def __init__(self, func): > self.__func = func > self.__executed = False > self.__value = None > > def __str__(self): > if self.__executed: > return self.__value.__str__() > self.__value = self.__func() > self.__executed = True > return self.__value.__str__() > > > def function_print(value): > print('function_print') > print(value) > > def function_return_stuff(value): > print('function_return_stuff') > return value > > function_print(function_return_stuff('no_delay')) > > function_print(Delayed(lambda: function_return_stuff('delayed'))) > > delayed = Delayed(lambda: function_return_stuff('delayed_object')) > function_print(delayed) > function_print(delayed) > > Unfortunately, due to > https://docs.python.org/3/reference/datamodel.html#special-lookup , this > magic delayed class would need to implement many magic methods, as > __getattribute__ is not _always_ called. > > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > > > > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/