Another use case for the 'lazy' (aka 'delayed') keyword

The debate on the 'lazy' keyword seems to have settled, but I don't know if somebody is trying to write a PEP about it. Anyway, I was doing something like this the other day: conf.get('setting_name', load_from_db('setting_name')) And then realized I could save a query not doing the load_from_db() call most of the time. But dict.get didn't accept callable so I couldn't do: conf.get('setting_name', lambda key: load_from_db('setting_name')) Which is standard practice in a lot of popular Python libs on Pypi. Instead I did: val = conf.get('setting_name') if val is None: val = load_from_db('setting_name') Which is way more verbose. It also has a bug if None is a valid configuration value or if I expect my code to be thread safe. It was not a problem for me, but in that case one would even have to do: try: val = conf['setting_name'] except KeyError: val = load_from_db('setting_name') Which is even more verbose. I was going to suggest to python-ideas to update the dict.get() signature to accept a callable, but it would break compatibility with many code actually getting a callable. We could do it for Python 4, but it's far way. Instead, I think it's a good example of were 'lazy' could help. You can't get simpler than: conf.get('setting_name', lazy load_from_db('setting_name'))

On Tue, Feb 28, 2017 at 11:04 PM, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
Alternatively, you could define 'conf' as a subclass of dict with a __missing__ method: class Config(dict): def __missing__(self, key): self[key] = load_from_db(key) return self[key] conf = Config() Then it becomes even simpler AND less redundant: conf['setting_name'] ChrisA

Le 28/02/2017 à 13:19, Chris Angelico a écrit :
Yes but this assumes: - I have access to the code instantiating conf; - all code using conf are using load_from_db as a default value; - load_from_db exists for all code using the conf object There is always a solution to all problems, as Python is turing complete. You don't need list comprehension, you can use a for loop. You don't need upacking you can uses indexing. And you don't need lazy, it's just convenient.

Le 28/02/2017 à 13:27, Markus Meskanen a écrit :
Well you could just use a dict subclass here with get() that takes callable...
As I said to Angelico: Yes but this assumes: - I have access to the code instantiating conf; - all code using conf are using load_from_db as a default value; - load_from_db exists for all code using the conf object There is always a solution to all problems, as Python is turing complete. You don't need list comprehension, you can use a for loop. You don't need upacking you can use indexing. And you don't need lazy, it's just convenient.

With this sole argument you can add almost anything to the language as long as it has at least one use case where it's better than existing tools. I'm not against the lazy proposal in general, but attempting to justify a new keyword to replace this is just silly to me: class Config(dict): def lazy_get(self, key, callable=None): if key in self: return self[key] return callable(key)

Le 28/02/2017 à 13:47, Markus Meskanen a écrit :
With this sole argument you can reject await / async because we could do it with yield from and decorator. With this solve argument you can reject comprehension because we have map and filter. With this solve argument you can reject defauldict because we can code one ourself. I mean come on, a decorator is just: @decorator def decorated(): Instead of decorated = decorator(decorated) It's just convenience and elegance. The sweet spot for lazy is a use case where you can't easily modify the code to get your getter and setter. You example again makes the assumption you can easily add such a wrapper. But if you can't, then you have to import it and wrap the config object in every module using it. Test all that. Ensure you don't pass the wrapper by accident to something that would break on it, etc. And how many time do you have to do that ? You will write your wrapper for lists ? And tuples ? And will you test those ? document those ? Explain to others that to get a callable, you need to pass a callable returning a callable ? Then again for your next project or you do a library with all the stuff it implies ? It's neither convenient nor elegant. lazy is not only practical, but it's also beautiful. It reads well. It solves a problem we all have on a regular basis. Yes we can live without it. I mean, Python is already incredibly convenient, of course whatever we suggest now is going to be a cherry on top of the language cake. And yes I got a big mouth because it's a lot of work to implement it and it's work I'm not capable to do since I can't code in C to save my life. Does it removes the merits from lazy ? Wouldn't you like it to just be able to drop "lazy" in your next 3.7 code ?

Yes I do think this is precisely where a lazy or delayed feature would work well. I'm working on writing up a complete PEP now, and have made some progress on the things left unsolved in the other thread. I will post again when the PEP is closer to complete. Fundamentally, anything can be done, but I don't think that altering the code of others for one case is Pythonic, and though it happens, I think there is value in making it less necessary. Cheers! Joseph

If you'd like help with the PEP, I'd be happy to assist (subject to time, as always the problem). I think this idea got pretty good support in the discussion (of course, I'm biased because it's one of the few proposed here that I actually really like). Few core developers really chimed in on the matter, so it's hard to say what they might opine about the implementation or supportability. On Tue, Feb 28, 2017 at 10:08 AM, Joseph Hackman <josephhackman@gmail.com> wrote:
-- 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.

On 28.02.2017 15:46, Michel Desmoulin wrote:
lazy is not only practical, but it's also beautiful. It reads well. It solves a problem we all have on a regular basis.
The only practical use case I ever ran into for lazy evaluation are lazy imports, simply because imports cause a lot of startup overhead. It's a case which the new keyword wouldn't help with, though. For the discussion, it would help if you'd write up a definition of where the lazy evaluation should finally happen, which use cases would be allowed or not and how the compiler could detect these. IMO, there are much better ways to write code which only evaluates expensive code when really needed. I don't see how "lazy" could automate this in a well defined, helpful and obvious way, simply because the side effects of moving evaluation from the place of definition to an arbitrary other place in the code are not easy to manage. Even if you do manage to clearly define when to evaluate a lazy expression, the context in which it gets evaluated may have already changed, giving wrong results, causing exceptions due to missing information. Delaying exceptions can also break the exception handling in your application. PS: You run into the same issues with lazy imports, which is why special care has to be taken when using these. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

Clearly there is SOME semantics that is consistent and coherent since many languages have either a lazy or eager declaration syntax, with different defaults between languages but both being built in. There are a lot of ways that Python isn't Haskell, obviously. But both Scheme and OCaml are eager by default with a lazy declaration (and Haskell or Miranda have an eager declaration correspondingly). It might be worth looking at their semantics in the PEP. On Tue, Feb 28, 2017 at 7:57 AM, M.-A. Lemburg <mal@egenix.com> wrote:
-- 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.

On 28.02.2017 17:35, David Mertz wrote:
Scheme, for example, uses an explicit approach to turning a promise into a value: http://www.shido.info/lisp/scheme_lazy_e.html This makes a lot of sense, but you can already have the same in Python using generators. Haskell is lazy by default, but this only works because you can pass parameters by name rather than value. https://wiki.haskell.org/Lazy_evaluation In Python we only have pass by value (parameters to functions get pushed onto the VM stack). Pass by name can also be had, but requires explicit indirection, e.g. by using a generator as wrapper.
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On Tue, Feb 28, 2017 at 8:54 AM, M.-A. Lemburg <mal@egenix.com> wrote:
I think the closer equivalent is a lambda. And it's quite true that Scheme's `force` is pretty much the same thing as making a call to the lambda later. E.g. def force(x): if callable(x): return x() else: return x In Python we only have pass by value (parameters to functions
I wouldn't want to change pass-by-value semantics. What I imagine instead is a new type `lazy` or `delayed` or `deferred` (whatever spelling). Delayed objects would have that type, and access to objects would need to do an implementation-level call to a "_force" that looks something like: def _force(x): if isinstance(x, delayed): return _concretize(x) else: return x I *do* see the obvious likely problem here. Object access is perhaps the most common thing that happens in Python, and adding a new conditional check to every one of those could very well slow down all the non-lazy behavior far too much. -- 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.

On 28.02.2017 17:54, M.-A. Lemburg wrote:
Here's an example similar to OCaml's lazy evaluation, which uses a simple lazy proxy object. import sys import time ### OCaml like lazy evaluation class Lazy: def __init__(self, code, frame): self.code = code self.globals = frame.f_globals self.locals = frame.f_locals def force(self): return eval(self.code, self.globals, self.locals) def lazy(code): return Lazy(code, sys._getframe(1)) ### def log(level, b, c): if level > 100: return if isinstance(c, Lazy): c = c.force() print ('%04i: %s' % (level, b % c)) def expensive(x): time.sleep(2.0) return x value = 1 log(1000, 'Hello %i', lazy("expensive(value)")) log(10, 'Error %i', lazy("expensive(value)")) ### Everything is nice and explicitly defined in the example. You can see where the deferred evaluation is requested and where it's eventually run. There are no surprises. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On Tue, Feb 28, 2017 at 9:30 AM, M.-A. Lemburg <mal@egenix.com> wrote:
This is a nice implementation. def log(level, b, c):
I agree this is very explicit. It's also more code which has a minor smell to it. In the hypothetical new construct I think it would look like this: def log(level, b, c): if level <= 100: print ('%04i: %s' % (level, b % c)) log(1000, 'Hello %i', delayed expensive(value)) log(10, 'Error %i', delayed expensive(value)) To me the second reads much better. But indeed there *might be* surprises. We might access the delayed object somewhere other than in the log() function (not in this example since it's not named, but in other cases). Understanding where that happens adds to debugging work. Where I believe this is more important is for Dask-like code structures. So perhaps I write: x = delayed expensive(1,2) y = delayed expensive(x,7) z = delayed expensive(x,y) w = delayed expensive(y,z) v = delayed expensive(w,x) print(z) If I want to concretize `z` I don't want to do all the work of also concretizing `w` and `v` (but I need to do `x` and `y`). In your explicit proxy approach, a lot of extra code is needed here. But maybe Dask's spelling is perfectly fine: from dask import delayed x = delayed(expensive)(1,2) y = delayed(expensive)(x,7) z = delayed(expensive)(x,y) w = delayed(expensive)(y,z) v = delayed(expensive)(w,x) print(z.compute()) -- 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.

This could be solved with a null-coalescing operator, e.g. PEP-505.
val = conf.get('setting_name') ?? load_from_db('setting_name')
The right side isn't evaluated unless the left side is None. It's similar to this:
val = conf.get('setting_name') or load_from_db('setting_name')
Except that using "or" would result in any false-y value (0, "", [], etc.) being overridden by the result of `load_from_db(...)`. I'm not strongly opposed to "lazy", but I think null-coalescing is easier to reason about. On Tue, Feb 28, 2017 at 7:04 AM, Michel Desmoulin <desmoulinmichel@gmail.com
wrote:

Only if "None" is not a proper value from .get(). And this doest solve the problems with more general callbacks that are not trying to get a value (cf Django lazy_gettext). Besides, "??" does not exist yet for Python, so let's counter the argument for lazy with the potential benefits of something we don't have ATM. A proposal is already hard enough to defenD on Python-ideas :) Le 28/02/2017 à 16:21, Mark E. Haase a écrit :

There are plenty of PEP-505 threads in the archives, plus the PEP itself. I was only pointing out that the use case in thread might also be solved with an existing proposal*, so I won't derail this thread any further. *There are actually several proposals for short-circuit evaluation, including PEP-531 and PEP-532. On Tue, Feb 28, 2017 at 10:29 AM, Joseph Hackman <josephhackman@gmail.com> wrote:

On Tue, Feb 28, 2017 at 11:04 PM, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
Alternatively, you could define 'conf' as a subclass of dict with a __missing__ method: class Config(dict): def __missing__(self, key): self[key] = load_from_db(key) return self[key] conf = Config() Then it becomes even simpler AND less redundant: conf['setting_name'] ChrisA

Le 28/02/2017 à 13:19, Chris Angelico a écrit :
Yes but this assumes: - I have access to the code instantiating conf; - all code using conf are using load_from_db as a default value; - load_from_db exists for all code using the conf object There is always a solution to all problems, as Python is turing complete. You don't need list comprehension, you can use a for loop. You don't need upacking you can uses indexing. And you don't need lazy, it's just convenient.

Le 28/02/2017 à 13:27, Markus Meskanen a écrit :
Well you could just use a dict subclass here with get() that takes callable...
As I said to Angelico: Yes but this assumes: - I have access to the code instantiating conf; - all code using conf are using load_from_db as a default value; - load_from_db exists for all code using the conf object There is always a solution to all problems, as Python is turing complete. You don't need list comprehension, you can use a for loop. You don't need upacking you can use indexing. And you don't need lazy, it's just convenient.

With this sole argument you can add almost anything to the language as long as it has at least one use case where it's better than existing tools. I'm not against the lazy proposal in general, but attempting to justify a new keyword to replace this is just silly to me: class Config(dict): def lazy_get(self, key, callable=None): if key in self: return self[key] return callable(key)

Le 28/02/2017 à 13:47, Markus Meskanen a écrit :
With this sole argument you can reject await / async because we could do it with yield from and decorator. With this solve argument you can reject comprehension because we have map and filter. With this solve argument you can reject defauldict because we can code one ourself. I mean come on, a decorator is just: @decorator def decorated(): Instead of decorated = decorator(decorated) It's just convenience and elegance. The sweet spot for lazy is a use case where you can't easily modify the code to get your getter and setter. You example again makes the assumption you can easily add such a wrapper. But if you can't, then you have to import it and wrap the config object in every module using it. Test all that. Ensure you don't pass the wrapper by accident to something that would break on it, etc. And how many time do you have to do that ? You will write your wrapper for lists ? And tuples ? And will you test those ? document those ? Explain to others that to get a callable, you need to pass a callable returning a callable ? Then again for your next project or you do a library with all the stuff it implies ? It's neither convenient nor elegant. lazy is not only practical, but it's also beautiful. It reads well. It solves a problem we all have on a regular basis. Yes we can live without it. I mean, Python is already incredibly convenient, of course whatever we suggest now is going to be a cherry on top of the language cake. And yes I got a big mouth because it's a lot of work to implement it and it's work I'm not capable to do since I can't code in C to save my life. Does it removes the merits from lazy ? Wouldn't you like it to just be able to drop "lazy" in your next 3.7 code ?

Yes I do think this is precisely where a lazy or delayed feature would work well. I'm working on writing up a complete PEP now, and have made some progress on the things left unsolved in the other thread. I will post again when the PEP is closer to complete. Fundamentally, anything can be done, but I don't think that altering the code of others for one case is Pythonic, and though it happens, I think there is value in making it less necessary. Cheers! Joseph

If you'd like help with the PEP, I'd be happy to assist (subject to time, as always the problem). I think this idea got pretty good support in the discussion (of course, I'm biased because it's one of the few proposed here that I actually really like). Few core developers really chimed in on the matter, so it's hard to say what they might opine about the implementation or supportability. On Tue, Feb 28, 2017 at 10:08 AM, Joseph Hackman <josephhackman@gmail.com> wrote:
-- 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.

On 28.02.2017 15:46, Michel Desmoulin wrote:
lazy is not only practical, but it's also beautiful. It reads well. It solves a problem we all have on a regular basis.
The only practical use case I ever ran into for lazy evaluation are lazy imports, simply because imports cause a lot of startup overhead. It's a case which the new keyword wouldn't help with, though. For the discussion, it would help if you'd write up a definition of where the lazy evaluation should finally happen, which use cases would be allowed or not and how the compiler could detect these. IMO, there are much better ways to write code which only evaluates expensive code when really needed. I don't see how "lazy" could automate this in a well defined, helpful and obvious way, simply because the side effects of moving evaluation from the place of definition to an arbitrary other place in the code are not easy to manage. Even if you do manage to clearly define when to evaluate a lazy expression, the context in which it gets evaluated may have already changed, giving wrong results, causing exceptions due to missing information. Delaying exceptions can also break the exception handling in your application. PS: You run into the same issues with lazy imports, which is why special care has to be taken when using these. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

Clearly there is SOME semantics that is consistent and coherent since many languages have either a lazy or eager declaration syntax, with different defaults between languages but both being built in. There are a lot of ways that Python isn't Haskell, obviously. But both Scheme and OCaml are eager by default with a lazy declaration (and Haskell or Miranda have an eager declaration correspondingly). It might be worth looking at their semantics in the PEP. On Tue, Feb 28, 2017 at 7:57 AM, M.-A. Lemburg <mal@egenix.com> wrote:
-- 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.

On 28.02.2017 17:35, David Mertz wrote:
Scheme, for example, uses an explicit approach to turning a promise into a value: http://www.shido.info/lisp/scheme_lazy_e.html This makes a lot of sense, but you can already have the same in Python using generators. Haskell is lazy by default, but this only works because you can pass parameters by name rather than value. https://wiki.haskell.org/Lazy_evaluation In Python we only have pass by value (parameters to functions get pushed onto the VM stack). Pass by name can also be had, but requires explicit indirection, e.g. by using a generator as wrapper.
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On Tue, Feb 28, 2017 at 8:54 AM, M.-A. Lemburg <mal@egenix.com> wrote:
I think the closer equivalent is a lambda. And it's quite true that Scheme's `force` is pretty much the same thing as making a call to the lambda later. E.g. def force(x): if callable(x): return x() else: return x In Python we only have pass by value (parameters to functions
I wouldn't want to change pass-by-value semantics. What I imagine instead is a new type `lazy` or `delayed` or `deferred` (whatever spelling). Delayed objects would have that type, and access to objects would need to do an implementation-level call to a "_force" that looks something like: def _force(x): if isinstance(x, delayed): return _concretize(x) else: return x I *do* see the obvious likely problem here. Object access is perhaps the most common thing that happens in Python, and adding a new conditional check to every one of those could very well slow down all the non-lazy behavior far too much. -- 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.

On 28.02.2017 17:54, M.-A. Lemburg wrote:
Here's an example similar to OCaml's lazy evaluation, which uses a simple lazy proxy object. import sys import time ### OCaml like lazy evaluation class Lazy: def __init__(self, code, frame): self.code = code self.globals = frame.f_globals self.locals = frame.f_locals def force(self): return eval(self.code, self.globals, self.locals) def lazy(code): return Lazy(code, sys._getframe(1)) ### def log(level, b, c): if level > 100: return if isinstance(c, Lazy): c = c.force() print ('%04i: %s' % (level, b % c)) def expensive(x): time.sleep(2.0) return x value = 1 log(1000, 'Hello %i', lazy("expensive(value)")) log(10, 'Error %i', lazy("expensive(value)")) ### Everything is nice and explicitly defined in the example. You can see where the deferred evaluation is requested and where it's eventually run. There are no surprises. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On Tue, Feb 28, 2017 at 9:30 AM, M.-A. Lemburg <mal@egenix.com> wrote:
This is a nice implementation. def log(level, b, c):
I agree this is very explicit. It's also more code which has a minor smell to it. In the hypothetical new construct I think it would look like this: def log(level, b, c): if level <= 100: print ('%04i: %s' % (level, b % c)) log(1000, 'Hello %i', delayed expensive(value)) log(10, 'Error %i', delayed expensive(value)) To me the second reads much better. But indeed there *might be* surprises. We might access the delayed object somewhere other than in the log() function (not in this example since it's not named, but in other cases). Understanding where that happens adds to debugging work. Where I believe this is more important is for Dask-like code structures. So perhaps I write: x = delayed expensive(1,2) y = delayed expensive(x,7) z = delayed expensive(x,y) w = delayed expensive(y,z) v = delayed expensive(w,x) print(z) If I want to concretize `z` I don't want to do all the work of also concretizing `w` and `v` (but I need to do `x` and `y`). In your explicit proxy approach, a lot of extra code is needed here. But maybe Dask's spelling is perfectly fine: from dask import delayed x = delayed(expensive)(1,2) y = delayed(expensive)(x,7) z = delayed(expensive)(x,y) w = delayed(expensive)(y,z) v = delayed(expensive)(w,x) print(z.compute()) -- 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.

This could be solved with a null-coalescing operator, e.g. PEP-505.
val = conf.get('setting_name') ?? load_from_db('setting_name')
The right side isn't evaluated unless the left side is None. It's similar to this:
val = conf.get('setting_name') or load_from_db('setting_name')
Except that using "or" would result in any false-y value (0, "", [], etc.) being overridden by the result of `load_from_db(...)`. I'm not strongly opposed to "lazy", but I think null-coalescing is easier to reason about. On Tue, Feb 28, 2017 at 7:04 AM, Michel Desmoulin <desmoulinmichel@gmail.com
wrote:

Only if "None" is not a proper value from .get(). And this doest solve the problems with more general callbacks that are not trying to get a value (cf Django lazy_gettext). Besides, "??" does not exist yet for Python, so let's counter the argument for lazy with the potential benefits of something we don't have ATM. A proposal is already hard enough to defenD on Python-ideas :) Le 28/02/2017 à 16:21, Mark E. Haase a écrit :

There are plenty of PEP-505 threads in the archives, plus the PEP itself. I was only pointing out that the use case in thread might also be solved with an existing proposal*, so I won't derail this thread any further. *There are actually several proposals for short-circuit evaluation, including PEP-531 and PEP-532. On Tue, Feb 28, 2017 at 10:29 AM, Joseph Hackman <josephhackman@gmail.com> wrote:
participants (8)
-
Chris Angelico
-
David Mertz
-
Joseph Hackman
-
M.-A. Lemburg
-
Mark E. Haase
-
Markus Meskanen
-
Michel Desmoulin
-
MRAB