<div dir="ltr">Shouldn't the key function be called with ``key(*args, **kwargs)``?<div><br></div><div>It'd be helpful to see the entire revision, rather than just the diff. It's easier for me to read at least.</div><div><br><div><br><div class="gmail_quote"><div dir="ltr">On Sun, Jan 10, 2016, 3:27 AM Bill Winslow <<a href="mailto:bunslow@gmail.com" target="_blank">bunslow@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div dir="ltr">Sorry for the late reply everyone.<div><br></div><div>I think relying on closures, while a solution, is messy. I'd still much prefer a way to tell lru_cache to merely ignore certain arguments. I'll use some variant of the storing-the-partial-progress-as-an-attribute-on-the-cached-recursive-function method (either with the cached-recursive hidden from the top level via the try/except stuff, or with a more simple wrapper function).</div><div><br></div><div>I've further considered my original proposal, and rather than naming it "arg_filter", I realized that builtins like sorted(), min(), max(), etc all already have the exact same thing -- a "key" argument which transforms the elements to the user's purpose. (In the sorted/min/max case, it's called on the elements of the argument rather than the argument itself, but it's still the same concept.) So basically, my original proposal with renaming from arg_filter to key, is tantamount to extending the same functionality from sorted/min/max to lru_cache as well. As has been pointed out, my own use case is almost certainly *not* the only use case. The implementation and interface are both simple, and simpler than the alternatives which I'll rely on for now (wrappers and closures, or worse, global singletons etc). I would still like to see it in the stdlib in the future. I've appended a largely similar patch with the proposed additions (there's some internal variable renaming to avoid confusion, resulting in a longer diff).</div><div><br></div><div>Thanks again for all the input.</div><div><br></div><div>-Bill</div><div><br></div><div>-----------------------------------------------------------------------------------------------------------------</div><div><a href="https://hg.python.org/cpython/file/3.5/Lib/functools.py" style="font-size:12.8px" target="_blank">https://hg.<span>python</span>.org/cpython/file/3.5/Lib/functools.py</a><br></div><div><br></div><div><br></div><div><div>diff functools.py.orig functools.py</div><span><div>363c363</div><div>< def _make_key(args, kwds, typed,</div></span><div>---</div><div>> def _make_key(args, kwds, typed, key,</div><div>377c377,379</div><div><     key = args</div><div>---</div><div>>     if key is not None:</div><div>>         args, kwds = key(args, kwds)</div><div>>     cache_key = args</div><div>380c382</div><div><         key += kwd_mark</div><div>---</div><div>>         cache_key += kwd_mark</div><div>382c384</div><div><             key += item</div><div>---</div><div>>             cache_key += item</div><div>384c386</div><div><         key += tuple(type(v) for v in args)</div><div>---</div><div>>         cache_key += tuple(type(v) for v in args)</div><div>386,389c388,391</div><div><             key += tuple(type(v) for k, v in sorted_items)</div><div><     elif len(key) == 1 and type(key[0]) in fasttypes:</div><div><         return key[0]</div><div><     return _HashedSeq(key)</div><div>---</div><div>>             cache_key += tuple(type(v) for k, v in sorted_items)</div><div>>     elif len(cache_key) == 1 and type(cache_key[0]) in fasttypes:</div><div>>         return cache_key[0]</div><div>>     return _HashedSeq(cache_key)</div><div>391c393</div><div>< def lru_cache(maxsize=128, typed=False):</div><div>---</div><div>> def lru_cache(maxsize=128, typed=False, key=None):</div><div>400a403,407</div><div>>     If *key* is not None, it must be a callable which acts on the arguments</div><div>>     passed to the function. Its return value is used in place of the actual</div><div>>     arguments. It works analogously to the *key* argument to the builtins</div><div>>     sorted, max, and min.</div><div>> </div><div>421a429,431</div><div>>     if key is not None and not callable(key):</div><div>>         raise TypeErrpr('Expected key to be a callable')</div><div>> </div><div>423c433,434</div><span><div><         wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)</div><div>---</div></span><div>>         wrapper = _lru_cache_wrapper(user_function, maxsize, typed, key,</div><div>>                                      _CacheInfo)</div><div>428c439</div><span><div>< def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):</div><div>---</div></span><div>> def _lru_cache_wrapper(user_function, maxsize, typed, key, _CacheInfo):</div><div>456,457c467,468</div><span><div><             key = make_key(args, kwds, typed)</div></span><div><             result = cache_get(key, sentinel)</div><div>---</div><div>>             cache_key = make_key(args, kwds, typed, key)</div><div>>             result = cache_get(cache_key, sentinel)</div><div>462c473</div><div><             cache[key] = result</div><div>---</div><div>>             cache[cache_key] = result</div><div>471c482</div><span><div><             key = make_key(args, kwds, typed)</div></span><div>---</div><div>>             cache_key = make_key(args, kwds, typed, key)</div><div>473c484</div><div><                 link = cache_get(key)</div><div>---</div><div>>                 link = cache_get(cache_key)</div><div>487c498</div><div><                 if key in cache:</div><div>---</div><div>>                 if cache_key in cache:</div><div>496c507</div><div><                     oldroot[KEY] = key</div><div>---</div><div>>                     oldroot[KEY] = cache_key</div><div>513c524</div><div><                     cache[key] = oldroot</div><div>---</div><div>>                     cache[cache_key] = oldroot</div><div>517,518c528,529</div><div><                     link = [last, root, key, result]</div><div><                     last[NEXT] = root[PREV] = cache[key] = link</div><div>---</div><div>>                     link = [last, root, cache_key, result]</div><div>>                     last[NEXT] = root[PREV] = cache[cache_key] = link</div></div></div><div><div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Dec 30, 2015 at 11:10 PM, Michael Selik <span dir="ltr"><<a href="mailto:mike@selik.org" target="_blank">mike@selik.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><span><div class="gmail_quote"><div dir="ltr">On Tue, Dec 29, 2015 at 2:14 AM Franklin? Lee <<a href="mailto:leewangzhong%2Bpython@gmail.com" target="_blank">leewangzhong+python@gmail.com</a>> wrote:<br></div></div><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Sat, Dec 12, 2015 at 1:34 PM, Michael Selik <<a href="mailto:mike@selik.org" target="_blank">mike@selik.org</a>> wrote:<br>
> On Fri, Dec 11, 2015, 8:20 PM Franklin? Lee <<a href="mailto:leewangzhong%2Bpython@gmail.com" target="_blank">leewangzhong+python@gmail.com</a>><br>
> wrote:<br></blockquote></div></div></span><span><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">> This whole thing is probably best implemented as two separate functions<br>
> rather than using a closure, depending on how intertwined the code paths are<br>
> for the shortcut/non-shortcut versions.<br>
<br>
I like the closure because it has semantic ownership: the inner<br>
function is a worker for the outer function.<br></blockquote></div></div></span><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"></blockquote><div><br></div><div>True, a closure has better encapsulation, making it less likely someone will misuse the helper function. On the other hand, that means there's less modularity and it would be difficult for someone to use the inner function. It's hard to know the right choice without seeing the exact problem the original author was working on.</div></div></div><div dir="ltr"><div class="gmail_quote"><div> </div></div></div><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
>> On Fri, Dec 11, 2015 at 8:01 PM, Franklin? Lee<br>
>> <<a href="mailto:leewangzhong%2Bpython@gmail.com" target="_blank">leewangzhong+python@gmail.com</a>> wrote:<br></span><span>>> > 1. Rewrite your recursive function so that the partial state is a<br>
>> > nonlocal variable (in the closure), and memoize the recursive part.<br>
><br>
> I'd flip the rare-case to the except block and put the normal-case in the<br>
> try block. I believe this will be more compute-efficient and more readable.<br>
<br>
The rare case is in the except block, though.<br></span></blockquote><div><br></div><div>You're correct. Sorry, I somehow misinterpreted the comment, "# To trigger the exception the first time" as indicating that code path would run only once.<br></div></div></div></div>
</blockquote></div><br></div>
</div></div></div><br></div>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a></blockquote></div></div></div></div>