This is a good idea but some cases need to be lazily evaluated. Without that property `once()` loses a lot of utility. In the case of Django some of the decorated functions create objects that cannot be instantiated until the Django settings have been loaded, which rules out calling them from the module scope.
I believe this use case of lazily initialising objects that cannot be created in the module scope is encountered by other projects. One example off the top of my head might be a database connection, or some other stateful client, that you want a single instance of but want to actually create at some point in the future. > On 26 Apr 2020, at 18:38, Guido van Rossum <gu...@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() > > print(pwd) > > On Sun, Apr 26, 2020 at 7:09 AM Tom Forbes <t...@tomforb.es > <mailto:t...@tomforb.es>> wrote: > Hello, > I would like to suggest adding a simple “once” method to functools. As the > name suggests, this would be a decorator that would call the decorated > function, cache the result and return it with subsequent calls. My rationale > for suggesting this addition is twofold: > > First: It’s fairly common to use `lru_cache()` to implement this behaviour. > We use this inside Django (example > <https://github.com/django/django/blob/77aa74cb70dd85497dbade6bc0f394aa41e88c94/django/forms/renderers.py#L19>), > internally in other projects at my workplace, inside the stdlib itself > <https://github.com/python/cpython/blob/2fa67df605e4b0803e7e3aac0b85d851b4b4e09a/Lib/ipaddress.py#L1324> > and in numerous other projects. In the first few pages of a Github code > search > <https://github.com/search?l=Python&q=%22functools.lru_cache%22&type=Code> it > is fairly easy to find examples, any decorated method with no parameters is > using `lru_cache()` like `once()`. Using lru_cache like this works but it’s > not as efficient as it could be - in every case you’re adding lru_cache > overhead despite not requiring it. > > Second: Implementing this in Python, in my opinion, crosses the line of > “annoying and non-trivial enough to not want to repeatedly do it”. While a > naive (untested) implementation might be: > > def once(func): > sentinel = object() # in case the wrapped method returns None > obj = sentinel > @functools.wraps(func) > def inner(): > nonlocal obj, sentinel > if obj is sentinel: > obj = func() > return obj > return inner > > While to the people who are likely going to be reading this mailing this the > code above is understandable and potentially even somewhat simple. However to > a lot of people who might not have had experience with writing decorators or > understand sentinel objects and their use the above code might be > incomprehensible. A much more common, and in my opinion worse, implementation > that I’ve seen is something along the lines of this: > > _value = None > def get_value(): > nonlocal _value > if _value is None: > _value = some_function() > return _value > > Which is not ideal for obvious reasons. And these are not even including a > potentially key feature: locking the wrapped function so that it is only > called once if it is invoked from multiple threads at once. > > So, I’d like to propose adding a `once()` decorator to functools that: > 1. Has a C implementation, keeping the speed on-par with `lru_cache()` > 2. Ensures that the wrapped function is only called once when invoked by > multiple threads > > For some related discussion about this idea and lru_cache, please see my > thread on > <https://discuss.python.org/t/reduce-the-overhead-of-functools-lru-cache-for-functions-with-no-parameters/3956>discuss.python.org > <http://discuss.python.org/>. > _______________________________________________ > Python-ideas mailing list -- python-ideas@python.org > <mailto:python-ideas@python.org> > To unsubscribe send an email to python-ideas-le...@python.org > <mailto:python-ideas-le...@python.org> > https://mail.python.org/mailman3/lists/python-ideas.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/5OR3LJO7LOL6SC4OOGKFIVNNH4KADBPG/ > > <https://mail.python.org/archives/list/python-ideas@python.org/message/5OR3LJO7LOL6SC4OOGKFIVNNH4KADBPG/> > Code of Conduct: http://python.org/psf/codeofconduct/ > <http://python.org/psf/codeofconduct/> > > > -- > --Guido van Rossum (python.org/~guido <http://python.org/~guido>) > Pronouns: he/him (why is my pronoun here?) > <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
signature.asc
Description: Message signed with OpenPGP
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/WEKAVQMTHEQY3COOM3NY3UQAGPUO66DZ/ Code of Conduct: http://python.org/psf/codeofconduct/