Search code examples
pythonrecursiondecoratorpython-decoratorsfunctools

A few things about decorator functions


I am trying to understand an example of a Fibonacci sequence using a decorator to store values of already calculated numbers. For example fib(5) would be calculated, and when we got to fib(6), it wouldn't calculate fib(5) again... I understand decorators a little bit, but some things just confuse me. I have a few questions about the code below.

from functools import wraps
def dec(func):
    values = {}
    @wraps(func)
    def wrap(*args):
        if args not in values:
            values[args] = func(*args)
        return values[args]
    return wrap

@dec
def fib(n):
    if n <= 2:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)
  1. Why are *args used in wrap()? Isn't it supposed to just take a number n and check if the value of that is in the dictionary? Why are args in some places called with, and in some without the *?
  2. What happens when the function fib is called recursively (how does the decorator function behave). I first thought it enters that function during every recursion, but that can't be right because the values dictionary would reset? So does it then enter just the wrap() function?
  3. Why does it return wrap at the end?

Solution

  • 1- You're exactly right. There is no need for "*" as you are checking only the value that is passed to the function. So simply call it "n".

    2- First let's figure out what is label "fib" after you used "@dec" on top of it? Actually it's now your inner function inside your decorator(I mean "wrap" function). why ? because @dec actually doing this:

    fib = dec(fib)
    

    So "dec" decorator is called, what does it return? "wrap" function. What is "wrap" function? It's a closure which has that "values" dictionary.

    Whenever you call your decorator the body of the decorator executes only one time. So there is only one "values" dictionary. What else happens during executing the body of "dec" decorator? Nothing but returning the reference to "wrap" function. that's it.

    Now, when you call your "fib" function(originally "wrap" function), this closure runs normally as it's just a recursive function, except it has that extra caching functionality.

    3- Because you need to have a handle to the inner function(here "wrap" function). You wanna call it later in order to calculate Fibonacci.