lukewalker_p2501
lukewalker_p2501

Reputation: 1

Why is there no argument when returning function in a decorator.

Example: Fibonaci recursive function with memoize decorator. When calling function helper there is no argument. If function helper is defined to take an argument x therefore i expect calling the function with one argument. I would like to understand why is syntax like it is?

def memoize(f):
    memo = {}
    def helper(x):
        if x not in memo:            
            memo[x] = f(x)
        return memo[x]
    return helper

@memoize
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

print(fib(40))

Upvotes: 0

Views: 60

Answers (2)

alexis
alexis

Reputation: 50210

I believe the syntax is like this so that it looks as streamlined as possible. You put a decorator object after the @ (a function that takes one argument, and returns a function), and python calls it with the function you are defining. This

@memoize
def fib(n):
    ...

Is exactly equivalent to the following, which doesn't use the magic decorator syntax:

def fib(n):
   ...

fib = memoize(fib)

If you want your head to spin a little, consider that @ can actually be followed by a function call -- but this function call must return a decorator that works as above! Here's a silly example that counts how many times the decorated function is called, but lets you set the start value. (It's just an example: it's not very useful since only one function can be decorated, etc.)

def countcalls(start):
    global _calls
    _calls = start
    def decorator(f):
        def wrapper(x):
            global _calls
            _calls += 1
            return f(x)
        return wrapper
    return decorator

@countcalls(3)
def say(s):
    print(s)

say("hello")
# _calls is now 4

Here, countcalls(4) defines and returns (without calling it) the function decorator, which will wrap the decorated function and return the wrapper in place of the function I wrote.

Upvotes: 0

Eli Korvigo
Eli Korvigo

Reputation: 10503

You do call the helper with the argument. Decorators are syntactic sugar for this

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


fib = memoize(fib)

So your fib function is no longer the original fib-function. It is in fact the helper closure, because that's what memoize returns – a closure. So, when you call fib(40) you kind of call helper(40). The memoize decorator creates a function object, it doesn't call it, just returns it.

Upvotes: 2

Related Questions