从本质上分析装饰器
正规写法:
import time def timeslong(func): def call(): start = time.clock() print("It's time starting ! ") func() print("It's time ending ! ") end = time.clock() return "It's used : %s ." % (end - start) return call @timeslong def f(): y = 0 for i in range(10): y = y + i + 1 print(y) return y print(f())
等价写法:
import time def timeslong(func): def call(): start = time.clock() print("It's time starting ! ") func() print("It's time ending ! ") end = time.clock() return "It's used : %s ." % (end - start) return call def f(): y = 0 for i in range(10): y = y + i + 1 print(y) return y f = timeslong(f) print(f())
错误写法:
import time def timeslong(func): start = time.clock() print("It's time starting ! ") func() print("It's time ending ! ") end = time.clock() return "It's used : %s ." % (end - start) @timeslong def f(): y = 0 for i in range(10): y = y + i + 1 print(y) return y f() //-----------报错信息: Traceback (most recent call last): File "E:/python_pri/Fishc40.py", line 100, in <module> f() TypeError: 'str' object is not callable
如果timeslong并不是一个返回函数便签的高阶函数,那么就会报这个错误!
如果说f()的实现过程就是将f作为参数传入timeslong(),这样不应该报错,如:
timeslong(f),这样不仅不报错,而且实现同样的功能!
那,上面报错的原因是什么呢?我们得从本质上分析:
@timeslong
def f():
pass
上面这段代码等价于,下面这段代码:
def f():
pass
f = timeslong(f)
现在同名的f变量指向了新的函数,于是调用f()将执行新函数——timeslong(f)。
那么,有鱼油就问了,既然f都赋了新的值,那么为什么timeslong(f)中的f调用的还是之前的f函数呢?
这个问题问的好:
原因是,f = timeslong(f)在执行的过程中,按照顺序先执行timeslong(f),再赋值f。在timeslong(f)这个过程中
,之前的f已经被保存到了timeslong函数的内部变量func中,当再次调用的f()的时候timeslong(f)中的是之前的f函数内容。
所以确切的说: @timeslong 等价于 f = timeslong(func)。才更为准确。
所以f()就等价于timeslong(func)()
再来说报错问题,就很明显了,如果timeslong不是一个高阶函数,按照上面的写法,timeslong返回的是一个
字符串,那么最终的结果可以看成:
f = timeslong(f) = 'str'
'str'()
字符串当然不会被调用,所以报错:
TypeError: 'str' object is not callable
作者:宋桓公
出处:http://www.cnblogs.com/douzi2/
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现