Search code examples
python-3.xfunctionobjectlogic

Can't understand assignments and environments usage on this recursive function


I'm learning about optimization in Composing Programs. I have 3 functions. memo is for memoization technique, count is a wrapper, so every time fib is called a counter is activated. fib is just an implementation of Fibonacci with recursivity.

 def memo(f):
    cache = {}

    def memoized(n):
        if n not in cache:
            cache[n] = f(n)
        return cache[n]

    return memoized


def count(f):
    def counted(*args):
        counted.call_count += 1
        return f(*args)

    counted.call_count = 0
    return counted


def fib(n):
    if n == 0:
        return 0
    if n == 1:
        return 1
    return fib(n - 2) + fib(n - 1)

To call the functions, I assing count(fib) to counted_fib, so every call is recorded. And* fib *variable is reasigned (now, fib is memo(counted_fib) not the original function)

counted_fib = count(fib)
fib = memo(counted_fib)

My problem start when the function fib is called (e.g. fib(19)) Everything works as it should be, but the recursion inside the original function (return fib(n - 2) + fib(n - 1)) trigger fib as memo(counted_fib).

This is not a problem about something that is not working. I'm just trying to understand why it happens if one thing is declared before another.


Solution

  • If what you are confused about is the way that the fib name behaves when being reassigned from the original fib() function to being a variable calling memo(counted_fib, I think this article about python's namespace behavior will help.

    Python programs are executed starting from the first statement. But even before that, the global namespace is loaded. So you assign the fib name to fib(). But later on, you change that assignment to memo(counted_fib), and that last assignment is the one loaded with the global namespace once the program starts.

    Hope it helps!