[Python-ideas] Using functools.lru_cache only on some arguments of a function

Michael Selik mike at selik.org
Sat Dec 12 13:34:22 EST 2015


On Fri, Dec 11, 2015, 8:20 PM Franklin? Lee <leewangzhong+python at gmail.com>
wrote:

> By the way, there are other usecases for ignoring arguments for
> caching. For example, dynamic programming where the arguments are the
> indices of a sequence, or some other object (tree?) which isn't a
> recursive argument. I recommend that those also be done with a closure
> (separating the recursive part from the initial arguments), but I
> think it's worth considering an lru_cache implementation for students
> who haven't learned to, er, abuse closures. Unless someone thinks a
> recipe can/should be added to the docs.
>

This whole thing is probably best implemented as two separate functions
rather than using a closure, depending on how intertwined the code paths
are for the shortcut/non-shortcut versions.

    @lru_cache
    def factorial(n):
        if n < 2:
            return 1
        return n * factorial(n-1)

    @lru_cache
    def factorial_faster(n, shortcut=None):
        if shortcut is not None:
            return shortcut
        return factorial(n)



> On Fri, Dec 11, 2015 at 8:01 PM, Franklin? Lee
> <leewangzhong+python at gmail.com> wrote:
> > Solutions:
> > 1. Rewrite your recursive function so that the partial state is a
> > nonlocal variable (in the closure), and memoize the recursive part.
>

I'd flip the rare-case to the except block and put the normal-case in the
try block. I believe this will be more compute-efficient and more readable.

    def factorial(n, answer=None):
        try:
            return factorial.recursive(n)
        except AttributeError:
            @lru_cache()
            def recursive(n):
                # shortcut
                if answer is not None:
                    return answer
                # non-shortcut
                if n < 2:
                    return 1
                return n * recursive(n-1)
            factorial.recursive = recursive
            return recursive(n)

Note that the original question was how to handle an optional shortcut
parameter that would not change the output but simply increase speed if
(and only if) that call was a cache miss. A successive cache hit should be
near instantaneous, regardless of the optional parameter.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20151212/8bd6d287/attachment.html>


More information about the Python-ideas mailing list