我正在学习Python 3,并希望编写一个简单的代码来学习装饰器。我试着运行这段代码:
def makebold(fn):
def wrapped():
return '<b>' + str(fn) + '</b>'
return wrapped()
def makeitalic(fn):
def wrapped():
return '<i>' + str(fn) + '</i>'
return wrapped()
@makebold
@makeitalic
def hello():
return "Hello World"
print(hello())
但是我收到以下错误:
Traceback (most recent call last):
File "E:\Msn Folder\92\Python\Projects\PythonSamplesByMsn\05 Functions\D03_Decorator01.py", line 29, in <module>
print(hello())
TypeError: 'str' object is not callable
我做错了什么?我怎么能纠正这个?
答案 0 :(得分:4)
您必须在()
结束时删除return wrapped()
并将str(fn)
更改为str(fn())
,因为必须调用fn
函数对象。
使用此
def makebold(fn):
def wrapped():
return '<b>' + str(fn()) + '</b>'
return wrapped
def makeitalic(fn):
def wrapped():
return '<i>' + str(fn()) + '</i>'
return wrapped
@makeitalic
@makebold
def hello():
return "Hello World"
print(hello())
<i><b>Hello World</b></i>
答案 1 :(得分:3)
首先,请注意
@bar
def foo():
...
只是简写:
foo = bar(foo)
即。你用函数本身调用bar
的结果替换函数。因此,在此之后,foo('baz')
确实是bar(foo)('baz')
,即尝试调用 bar
返回的任何内容。
正是由于这个原因,用作装饰器的函数的返回值本身必须是可调用的。但是,您的“装饰器”会返回字符串,它们不可调用。如果我们定义:
def bar(func):
return 'bar'
然后很明显bar(foo)('baz')
变为'bar'('baz')
,导致您看到的错误:
TypeError: 'str' object is not callable
您的装饰器返回字符串,因为您调用wrapped
,因此返回它返回的任何内容(字符串):
return wrapped()
# ^ note parentheses in your version
而不是返回函数本身:
return wrapped
# ^ and their absence when fixed
您遇到的第二个错误是wrapped
函数内部有:
return '<b>' + str(fn) + '</b>'
这里你有相反的问题 - 你正在创建一个函数本身的字符串,而不是使用函数返回的字符串。
请记住装饰器使用它作为参数装饰的函数调用,即装饰器内的fn
引用正在装饰的函数(例如hello
for {{ 1}})。你真正想要包装标签的是包装函数返回的内容(已经是一个字符串,所以你不需要调用makebold
:
str
您也可以使用正确的字符串格式,而不是return '<i>' + fn() + '</i>'
# ^ again, parentheses are important!
连接:
+
答案 2 :(得分:-1)
你应该试试这个:
def makebold(fn):
def wrapped():
return '<b>' + str(fn()) + '</b>'
return wrapped
def makeitalic(fn):
def wrapped():
return '<i>' + str(fn()) + '</i>'
return wrapped
@makebold
@makeitalic
def hello():
return "Hello World"
print(hello())
你应该在装饰器方法中调用包装函数。
装饰器方法必须返回一个函数对象。