0

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
bendaMan
  • 43
  • 7
  • 1
    The function that should do the printing in your case should reside in the `bar` decorator function, and it would be a function that would have its own internal function that would call the input function with the arguments that were passed, and those arguments you can then manage in the closure formed by `bar`. Please take a look at [decorator basics](https://stackoverflow.com/a/1594484/) on how decorators work. – metatoaster Aug 30 '20 at 09:08

1 Answers1

0

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
BoarGules
  • 16,440
  • 2
  • 27
  • 44
  • Seems to work just great, thank you :) Could you explain why `prev` keeps its value after each call and doesn't get reset? – bendaMan Aug 30 '20 at 18:58
  • It's a *mutable default parameter* and it is widely regarded as evil (Google it). Python sets up default parameter values when the function is defined, and they persist across calls. If it is a mutable object like a list, it is the *reference* that persists across calls to the function, and so it works much like what other languages call a static variable (but doesn't *look* like one). One use case is memoizing the results of expensive function calls, so that a function can return a precooked result if it is called a second time with the same argument, instead of doing the work over. – BoarGules Aug 30 '20 at 21:22