装饰器错误:NoneType对象不可调用

时间:2013-01-26 10:53:47

标签: python python-2.7 decorator python-decorators

我写了一个像这样的函数装饰器:

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()

我收到以下错误消息:

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

我通过将“foo()”替换为“foo”来实现它。但我仍然没有得到我预期的结果:

foo() called

似乎只调用foo函数一次。

请帮助我理解为什么会这样。

3 个答案:

答案 0 :(得分:7)

您应该返回包装函数本身,而不是结果

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

装饰器用装饰器的返回值替换装饰的项目:

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

相当于:

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

扩展为(在您的代码中):

foo = wrappedFunc()

因此您使用foo调用的结果替换了函数wrappedFunc(),而不是使用wrappedFunc本身。

答案 1 :(得分:3)

您需要删除

中的括号
return wrappedFunc

装饰器应该返回包装函数,而不是调用它。

通过此修复,代码生成:

foo() called
foo() called

答案 2 :(得分:0)

有点晚了,但希望能对您有所帮助,扩展为什么接受所提供的答案

由于错误状态,“ NoneType”表示我们尝试调用的实例/对象没有类型(它不是function / int / boolean / class / instance)。它的类型只是“无” 因此,总而言之,装饰器不过是闭包的扩展使用,其功能被视为一等公民(您可以在closures上获得详细信息 基本上,这意味着装饰器希望返回一个函数,例如在大多数情况下是包装器,而原始函数不会改变,并随装饰器一起调用

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)

当您以错误的方式调用包装函数而不是仅返回函数时,对foo()的调用就会发生这种情况

因此,对于一个装饰器来说,要按照规范运行,它应该返回一个包装函数,而原始函数将保持不变。因此,应根据接受的答案将其重写