7

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

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函数只被调用一次。

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

4

4 回答 4

18

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

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本身。

于 2013-01-26T10:55:27.217 回答
3

您需要删除括号

return wrappedFunc

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

使用此修复程序,代码会生成:

foo() called
foo() called
于 2013-01-26T10:56:06.073 回答
0

有点晚了,但希望这会有所帮助,扩展为什么提供的答案被接受

如错误所述,“NoneType”表示我们尝试调用的实例/对象没有类型(它不是函数/int/boolean/class/instance)。它的类型只是“无”所以总而言之,装饰器只不过是对闭包的扩展使用,其函数被视为一等公民(您可以获得关于闭包的详细视图 这基本上意味着装饰器希望返回一个函数,比如说大多数情况下的包装器,原始函数保持不变并使用装饰器调用

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

于 2020-06-26T14:00:06.870 回答
0

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()  # 
于 2021-08-27T14:54:11.423 回答