
On Apr 30, 2020, at 6:32 AM, Joao S. O. Bueno <jsbueno@python.org.br> wrote:
Of course this is meant to be something simple - so there are no "real world use cases" that are "wow, it could not have been done without it".
The proposed implementation does something risky, it hold holds a non-reentrant lock across a call to an arbitrary user-defined function. The only reason to do so is to absolutely guarantee the function will never be called twice. We really should look for some concrete examples that require that guarantee, and it would be nice to see how that guarantee is being implemented currently (it isn't obvious to me). Also, most initialization functions I've encountered take at least one argument, so the proposed call_once() implementation wouldn't be usable at all.
I was one of the first to reply to this on "python-ideas", as I often need the pattern, but seldon worrying about rentrancy, or parallel calling. Most of the uses are just that: initalize a resource lazily, and just "lru_cache" could work. My first thought was for something more light-weight than lru_cache (and a friendlier name).
Right. Those cases could be solved trivially if we added: call_once = lru_cache(maxsize=None) which is lightweight, very fast, and has a clear name. Further, it would work with multiple arguments and would not fail if the underlying function turned out to be reentrant. AFAICT, the *only* reason to not use the lru_cache() implementation is that in multithreaded code, it can't guarantee that the underlying function doesn't get called a second time while still executing the first time. If those are things you don't care about, then you don't need the proposed implementation; we can give you what you want by adding a single line to functools.
So, one of the points I'd likely have used this is here:
https://github.com/jsbueno/terminedia/blob/d97976fb11ac54b527db4183497730883...
Thanks — this is a nice example. Here's what it tells us: 1) There exists at least one use case for a zero argument initialization function 2) Your current solution is trivially easy, clear, and fast. "if CHAR_BASE: return". 3) This function returns None, so efforts by call_once() to block and await a result are wasted. 4) It would be inconsequential if this function were called twice. 5) A more common way to do this is to move the test into the lookup() function -- see below. Raymond ------------------------- CHAR_BASE = {} def _init_chars(): for code in range(0, 0x10ffff): char = chr(code) values = {} attrs = "name category east_asian_width" for attr in attrs.split(): try: values[attr] = getattr(unicodedata, attr)(char) except ValueError: values[attr] = "undefined" CHAR_BASE[code] = Character(char, code, values["name"], values["category"], values["east_asian_width"]) def lookup(name_part, chars_only=False): if not CHAR_BASE: _init_chars() results = [char for char in CHAR_BASE.values() if re.search(name_part, char.name, re.IGNORECASE)] if not chars_only: return results return [char.char for char in results]