> On Apr 29, 2020, at 12:55 AM, Tom Forbes <t...@tomforb.es> wrote:
> 
> Hey Raymond,
> Thanks for your input here! A new method wouldn’t be worth adding purely for 
> performance reasons then, but there is still an issue around semantics and 
> locking.

Right.


> it doesn’t actually ensure the function is called once.

Let's be precise about this.  The lru_cache() logic is:

1) if the function has already been called and result is known, return the 
prior result  :-)
2) call the underlying function
3) add the question/answer pair to the cache dict. 

You are correct that a lru_cache() wrapped function can be called more than 
once if before step three happens, the wrapped function is called again, either 
by another thread or by a reentrant call.  This is by design and means that 
lru_cache() can be wrapped around almost anything, reentrant or not.  Also 
calls to lru_cache() don't block across the function call, nor do they fail 
because another call is in progress.  This makes lru_cache() easy to use and 
reliable, but it does allow the possibility that the function is called more 
than once.

The call_once() decorator would need different logic:

1) if the function has already been called and result is known, return the 
prior result  :-)
2) if function has already been called, but the result is not yet known, either 
block or fail  :-(
3) call the function, this cannot be reentrant :-(
4) record the result for future calls.

The good news is that call_once() can guarantee the function will not be called 
more than once.  The bad news is that task switches during step three will 
either get blocked for the duration of the function call or they will need to 
raise an exception.    Likewise, it would be a mistake use call_once() when 
reentrancy is possible.

> The reason I bring this up is that I’ve seen several ad-hoc `call_once` 
> implementations recently, and creating one is surprisingly complex for 
> someone who’s not that experienced with Python.


Would it fair to describe call_once() like this?

call_once() is just like lru_cache() but:

1) guarantees that a function never gets called more than once
2) will block or fail if a thread-switch happens during a call
3) only works for functions that take zero arguments
4) only works for functions that can never be reentrant
5) cannot make the one call guarantee across multiple processes
6) does not have instrumentation for number of hits
7) does not have a clearing or reset mechanism


Raymond


_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/CTAGWXD7WRU3NAHLP5IZ75PM2E3TQTG2/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to