Search code examples
pythonclosuresdecoratorwrapperpython-decorators

using closures to remember previous function calls


I've started a new project of mine, and I'd like to create a function, we'll call it foo, which remembers the previous calls made to it.

So if for example the function should return the last parameter used we should have this:

print(foo(2), foo(7), foo('a'))

print out:

None 2 7

The Question

How can we create a decorator function, called bar for example, which prints the given arguments unless the previous function call is the same as the previous one, in that case the function prints nothing and simply returns None.

What did I manage to do?

So far I was able to create a skeleton of the function, looking like this:

@bar
def printer(*args):
    print(*args)

and in the bar function I should check the previous call somehow, but I don't know how! I don't want to use global variables or something but only use the function. I assume I need to use closures for the bar and foo funcs. Could you me how to implement them?

NOTE this is what should happen for these calls:

printer("a")  # prints a
printer("a")  # only returns None 
printer("a")  # only returns None
printer("b")  # prints b
printer("a")  # prints a

Solution

  • First write a version of foo that you want your decorator to produce, for example

    def foo(arg, prev=[]):
        if arg in prev[-1:]:
            return None
        else:
            prev.append(arg)
            return arg
    
    >>> print(foo(1), foo(2), foo(2), foo(3))
    1 2 None 3
    

    Now you know what you want the decorator to do.

    def bar(f):
        def bar(arg, prev=[]):
            if arg in prev[-1:]:
                return None
            else:
                prev.append(arg)
                return(f(arg))
        return bar
    

    Now redefine foo without the memory, but wrapped in the decorator:

    @bar
    def foo(arg):
        return arg
    
    >>> print(foo(1), foo(2), foo(2), foo(3))
    1 2 None 3