Some libraries implement a 'lazy object' which forwards all operations to a wrapped object, which gets lazily initialised once: https://github.com/ionelmc/python-lazy-object-proxy https://docs.djangoproject.com/en/3.0/_modules/django/utils/functional/ There's also a more general concept of proxying everything to some target. wrapt provides ObjectProxy which is the simplest case, the idea being that you override specific operations: https://wrapt.readthedocs.io/en/latest/wrappers.html Flask and werkzeug provide proxies which forward based on the request being handled, e.g. which thread or greenlet you're in, which allows magic like the global request object: https://flask.palletsprojects.com/en/1.1.x/api/#flask.request All of these have messy looking implementations and hairy edge cases. I imagine the language could be changed to make this kind of thing easier, more robust, and more performant. But I'm struggling to formulate what exactly "this kind of thing is", i.e. what feature the language could use. On Tue, Apr 28, 2020 at 8:02 PM Andrew Barnert via Python-ideas < python-ideas@python.org> wrote:
On Apr 26, 2020, at 10:41, Guido van Rossum <guido@python.org> wrote:
Since the function has no parameters and is pre-computed, why force all
users to *call* it? The @once decorator could just return the value of calling the function:
def once(func): return func()
@once def pwd(): return os.getcwd()
If that’s all @once does, you don’t need it. Surely this is even clearer:
pwd = os.getcwd()
The decorator has to add initialization on first demand, or it’s not doing anything.
But I think you’re onto something important that everyone else is missing. To the user of this module, this really should look like a variable, not a function. The fact that we want to initialize it later shouldn’t change that. Especially not in Python—other languages bend over backward to make you write getters around every public attribute even when you don’t need any computation; Python bends over backward to let you expose public attributes even when you do need computation.
And this isn’t unprecedented. Swift added a lazy variable initialization feature in version 2 even though they already had dispatch_once. And then they discovered that it eliminated nearly all good uses of dispatch_once and deprecated it. All you need is lazy-initialized variables. Your singletons, your possibly-unused expensive tables, your fiddly low-level things with complicated initialization order dependencies, they’re all lazy variables. So what’s left for @once functions that need to look like functions?
I think once you think about it in these terms, @lazy makes more sense than @once. The difference between these special attributes and normal ones is that they’re initialized on first demand rather than at definition time. The “on demand” is the salient bit, not the “first”.
The only problem is: how could this be implemented? Most of the time you want these things on modules. For lazy imports, the __getattr__ solution of PEP 562 was good enough, but this isn’t nearly as much of an expert feature. Novices write lazy variables in Swift, and if we have to tell them they can’t do it in Python without learning the deep magic of how variable lookup works, that would be a major shame. But I can’t think of an answer that doesn’t run into all the same problems that PEP 562’s competing protocols did. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/GHDFHZ... Code of Conduct: http://python.org/psf/codeofconduct/