I agree, that was the topic of my original post to the python-ideas discussion group[1]. I thought we should special-case `lru_cache()` to account for this use case.
The general consensus was that a `once()` decorator would be less confusing and more semantically correct than using `lru_cache` like this. I can also see the reasoning behind that point of view, and it could be further argued that using `lru_cache()` in a non-lru way might be more confusing. Perhaps we could alter the documentation to explicitly “bless” this approach, but as shown in the original python-ideas discussion people had strong negative opinions towards that approach. 1. https://discuss.python.org/t/reduce-the-overhead-of-functools-lru-cache-for-functions-with-no-parameters/3956 > On 26 Apr 2020, at 17:43, Alex Hall <alex.moj...@gmail.com> wrote: > > It's not clear to me why people prefer an extra function which would be > exactly equivalent to lru_cache in the expected use case (i.e. decorating a > function without arguments). It seems like a good way to cause confusion, > especially for beginners. Based on the Zen, there should be one obvious way > to do it. > > On Sun, Apr 26, 2020 at 6:33 PM Tom Forbes <t...@tomforb.es > <mailto:t...@tomforb.es>> wrote: >> What if the functions requires arguments? How to cache calls with >> different arguments? What if some arguments are not hashable? > Then I think lru_cache is perfectly suitable for that use case. `once()` > would only be useful if you’re calling a function with no arguments and > therefore return a constant value. I originally thought that an exception > could be raised if `@once()` was used with a function that accepted > arguments, but it might be better to instead simply ignore arguments instead? > It could help with some situations where your method accepts a single “self” > argument, or another value, that you know will be constant across calls. > >> Why ``functools``? Why not your own library or a package at PyPI. Like >> https://pypi.org/project/cachetools/ <https://pypi.org/project/cachetools/> ? > Because `lru_cache` fits the use case almost perfectly, is available in the > stdlib and is very, very fast. As such people are using it like they would > use `once()` which to me feels like a good argument to either special case > `lru_cache()` to account for this or explicitly add a complimentary `once()` > method alongside `lru_cache`. Adding a complimentary method seems better. > > On Sun, Apr 26, 2020 at 4:26 PM Oleg Broytman <p...@phdru.name > <mailto:p...@phdru.name>> wrote: > On Sun, Apr 26, 2020 at 03:03:16PM +0100, 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 > > > > <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 > > > > <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 > > <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() > > What if the functions requires arguments? How to cache calls with > different arguments? What if some arguments are not hashable? > > Why ``functools``? Why not your own library or a package at PyPI. Like > https://pypi.org/project/cachetools/ <https://pypi.org/project/cachetools/> ? > > > 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 > > > > <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/> <http://discuss.python.org/ > > <http://discuss.python.org/>>. > > Oleg. > -- > Oleg Broytman https://phdru.name/ <https://phdru.name/> > p...@phdru.name <mailto:p...@phdru.name> > Programmers don't die, they just GOSUB without RETURN. > _______________________________________________ > 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/74ZGZRB4JF6EPVB5E7WHL44KZUGEUELV/ > > <https://mail.python.org/archives/list/python-ideas@python.org/message/74ZGZRB4JF6EPVB5E7WHL44KZUGEUELV/> > Code of Conduct: http://python.org/psf/codeofconduct/ > <http://python.org/psf/codeofconduct/> > _______________________________________________ > 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/2BDIXRRLGJD3JWNUJCCB227SGCQK2YN5/ > > <https://mail.python.org/archives/list/python-ideas@python.org/message/2BDIXRRLGJD3JWNUJCCB227SGCQK2YN5/> > Code of Conduct: http://python.org/psf/codeofconduct/ > <http://python.org/psf/codeofconduct/>
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/ANL3U3NMF7YYA77HAB224ARDYQPNCEO2/ Code of Conduct: http://python.org/psf/codeofconduct/