Question

I wrote a function decorator like this:

def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc()

@tsfunc
def foo():
    pass

foo()  # to get it work, use foo instead of foo()
foo()

I got following error message:

foo() called
Traceback (most recent call last):
  File "decorator.py", line 11, in <module>
    foo()
TypeError: 'NoneType' object is not callable

I get it work by replacing "foo()" with "foo". but I still didn't get the result I expected:

foo() called

seems like the foo function is only called once.

Please help me understand why this is happening.

Was it helpful?

Solution

You should return the wrapper function itself, not its result:

def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc   # Do not call the function, return a reference instead

Decorators replace the decorated item with the return value of the decorator:

@tsfunc
def foo():
    # ....

is equivalent to:

def foo():
    # ....
foo = tsfunc(foo)

which expands to (in your code):

foo = wrappedFunc()

so you were replacing the function foo with the result of the wrappedFunc() call, not with wrappedFunc itself.

OTHER TIPS

You need to remove the parentheses in

return wrappedFunc

The decorator is supposed to return the wrapper function, not call it.

With this fix, the code produces:

foo() called
foo() called

A little late , but hope this helps, Extending on why the answer provided is accepted

as the error states, 'NoneType' means that the instance/object we are trying to call has no type (it is not a function/int/boolean/class/instance ). Its type is just 'None' So to summarize, decorators are nothing but extended use of closures with functions being treated as first class citizens (You could get a detail view on closures It basically means a decorator expects a function to be returned , say a wrapper in most cases, with the original function being unaltered and being called withing the decorator

def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc() -> Here the function is not returned but called eventually

@tsfunc
def foo():
    pass

foo() - > calling this again doesnt make sense to trigger the decorator, since a reference for the method foo is enough. Hence foo works fine but foo() doesn't (method call has completed already but no value is returned) If you try like this, you would see that the variable has 'None' type

def tsfunc(func):
        def wrappedFunc():
            print '%s() called' % func.__name__
            return func()
        return wrappedFunc --  Here I made the correction
    
    @tsfunc
    def foo():
        pass
var1 = foo()
print(var1)

This what happened with the call to foo() when you had the incorrect way of calling the wrapper function rather than returning just the function

So for a decorator to function as per the norms, it should return a wrapper function with the original function being unaltered. And hence it should be rewritten as per the accepted answer

you use this syntax

def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc # not use wrappedFunc() becaues Because this function runs at the this time 

@tsfunc
def foo():
    pass

foo()  # 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top