Question

As stated in the memoization example in decorator's docs, you can't use the nested function approach to implement memoization while preserving the function signature. Instead, you have to lift the inner function out, and then create a trivial decorator function:

def _memoize(func, *args, **kwargs):
    # the memoization code

def memoize(f):
    f.cache = {}
    return decorator(_memoize, f)

Why can't I use an inner function? Or are the docs misleading, meaning there is a way to use an inner function with @decorator? Is there some sort of practical, implementation-based reason for why this is, or am I truly being forced to do it someone else's way? I detest helper functions, and would like to avoid this approach if possible; if there is a hack to get it working (without, of course, writing it from scratch myself), I'd like to hear what it is.

It should be noted that without the need for initializing the cache or any other code which would not be a part of the inner function, of course, @decorator works just fine by not using an outer function at all (but then again, why use an inner function when you have no code outside the inner function?).

Was it helpful?

Solution

Requiring this is an intentional design choice of the decorator library, explained in the documentation (emphasis in the original):

The difference with respect to the memoize_uw approach, which is based on nested functions, is that the decorator module forces you to lift the inner function at the outer level (flat is better than nested).

This is also explained right up in the motivation section of the Introduction:

For instance, typical implementations of decorators involve nested functions, and we all know that flat is better than nested.

Of course "flat is better than nested" is part of the Zen of Python. But you may disagree that it applies here, or may think that some other principle overrides it.

If you violently disagree with the design principles behind the library, you're probably not going to be happy with it.


If you look into the source code, you can see that the module makes some use of the assumption that you're going to be passing in top-level functions. For example, it copies func_globals, but does not try to copy non-local closure cells, and it expects inspect to have a module-level function to work with. In many cases, violating those assumptions won't actually hurt you. But if you insist on doing so, you will have to understand the code well enough to know when it will hurt you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top