IIRC, lru_cache actually stores a make_key function that it calls on the args and kw. If this were exposed as part of the interface, you could just do this: @lru_cache() def spam(a, b): return a _make_key = spam.cache.make_key def make_key(args, kw): return _make_key(args[:1], kw) spam.cache.make_key = make_key Your idea of being able to pass a key-transformer or -maker into the constructor is pretty nice too, but it seems like the two should work together instead of being completely different things that end up having the same effect. One last thing: your transformer obviously won't handle the case where the user passes the args by keyword instead of by name (and same for mine). Maybe they'd never do that for the input argument, but for partial_state it might be more readable than passing it positionally. So maybe we want to make it easier to correctly specify the args that matter. The simplest interface would just be a list of parameter names (and lru_cache would then have to get_signature its argument to figure out the positional equivalents). On the other hand, maybe the implementation complexity isn't worth the interface simplicity. Sent from my iPhone
On Dec 4, 2015, at 13:44, Bill Winslow <bunslow@gmail.com> wrote: This is a question I posed to reddit, with no real resolution: https://www.reddit.com/r/learnpython/comments/3v75g4/using_functoolslru_cach...
The summary for people here is the following:
Here's a pattern I'm using for my code:
def deterministic_recursive_calculation(input, partial_state=None): condition = do_some_calculations(input) if condition: return deterministic_recursive_calculation(reduced_input, some_state)
Basically, in calculating the results of the subproblem, the subproblem can be calculated quicker by including/sharing some partial results from the superproblem. (Calling the subproblem without the partial state still gives the same result, but takes substantially longer.)
I want to memoize this function for obvious reasons, but I need the lru_cache to ignore the partial_state argument, for its value does not affect the output, only the computation expense.
Is there any reasonable way to do this?
Things such as memoizing a wrapper don't actually solve the problem. About the only way I can think of with current technology is either to have a hidden singleton class which maintains state, or a hidden global variable, which amount to the same thing of storing the partial state outside the function. But those strike me as unwieldy and unpythonic.
What I'd really like to do is to have some way to tell functools.lru_cache to ignore some arguments of a function it's memoizing for the purposes of caching.
One way would be to add an "arg_filter" argument, which for purposes of this example would be used like so:
@lru_cache(arg_filter=lambda args, kwargs: args[:1], {}) def deterministic_recursive_calculation(input, partial_state=None): condition = do_some_calculations(input) if condition: return deterministic_recursive_calculation(reduced_input, some_state)
This effectively ignores all arguments except the first positional one for the purposes of caching. Such an option could be implemented as in the diff below (provided only for discussion purposes).
So what I want to know is: 1) Am I sane? Is there no particularly good way currently to go about caching functions following the given pattern? 2) Assuming the answer to 1) is "Yes I am sane, and there's no good way currently", is my proposal a reasonable one? Mostly on philosophical grounds, not necessarily specifics of how it works.
Thank you for your time and consideration.
Bill
---------------------------------------------------------------------------------------------------------------------------------- https://hg.python.org/cpython/file/3.5/Lib/functools.py
diff functools.py functools.py.orig 363c363 < def _make_key(args, kwds, typed, arg_filter, ---
def _make_key(args, kwds, typed, 377,378d376 < if arg_filter is not None: < args, kwds = arg_filter(args, kwds) 393c391 < def lru_cache(maxsize=128, typed=False, arg_filter=None):
def lru_cache(maxsize=128, typed=False): 403,406d400 < *arg_filter* is an optional function which filters user-speicified portions < of the arguments from the caching key (e.g. if an argument affects the < computation but not the final result). < 428,430d421 < if arg_filter is not None and not callable(arg_filter): < raise TypeError('Expected arg_filter to be a callable') < 432,433c423 < wrapper = _lru_cache_wrapper(user_function, maxsize, typed, arg_filter, < _CacheInfo)
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
438c428 < def _lru_cache_wrapper(user_function, maxsize, typed, arg_filter, _CacheInfo): ---
def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo): 466c456 < key = make_key(args, kwds, typed, arg_filter)
key = make_key(args, kwds, typed)
481c471 < key = make_key(args, kwds, typed, arg_filter) ---
key = make_key(args, kwds, typed)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/