我写了一个像这样的函数装饰器:
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
函数一次。
请帮助我理解为什么会这样。
答案 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()的调用就会发生这种情况
因此,对于一个装饰器来说,要按照规范运行,它应该返回一个包装函数,而原始函数将保持不变。因此,应根据接受的答案将其重写