浅谈python装饰器的实现原理
一、装饰器
在初学装饰器的时候,会觉得抽象生涩,其实装饰器就是对某个对象进行功能上的增强
下面详细讲一下 “增强” 的实现原理
二、增强的实现原理
其实原理就一句话:更改旧对象的指向,指向 构造的新的函数wrapper
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
>>> now()
call now():
2015-3-25
上面是拿廖雪峰-python-装饰器中的例子,在此做一个详细说明:
a. python解释器在执行now()
之前,首先加载环境,加载内存中对应的程序
b. 找到后,自上而下执行,于是先执行 @log
即执行: now = log(now)
我们来详细看一下log(now)发生了什么,重点关注指向对象的变化
1、首先解释器根据变量名log,找到内存中对应的程序
2、now函数传入log函数,重点来了,参数func和now 此时指向同一个对象,即内存中存储的
```
def now():
print('2015-3-25')
```
3、此时解释器继续向下读取程序,直到遇到return返回。这里要注意,
此时调用的是log函数,log内部的wrapper函数(闭包)此时并没有被调用,
所以此时执行log函数的return语句`return wrapper`
log函数将`wrapper`返回
c. log(now)返回wrapper
,然后赋值给now,重点来了,now变量指向的对象从
def now():
print('2015-3-25')
变为
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
d. abc结束后,解释器执行完@log,接下来读取到now()语句,开始执行:
1. 此时now已经和wrapper指向同一个对象,这里多说一句,其实此时如果运行
```
>>> now.__name__
'wrapper'
```
如果想要避免这种情况,可以使用@functools.wraps(func)
2. 我们继续执行,遇到`print('call %s():' % func.__name__)`,执行print内容
3. 遇到`return func(*args, **kw)`,还记得吗,上面我们提到func指向的对象是谁?
4. 调用func
5. 解释器找到func指向的程序,
```
def now():
print('2015-3-25')
```
6. 执行 `print('2015-3-25')`
e. 至此,now()执行结束,根据abcd的说明,大家应该可以理解返回的结果了
>>> now()
call now():
2015-3-25
三、说明
为了解释的通俗易懂,请大家忽略上面一些话术用语的不准确。
这章内容没有涉及到装饰器的“外函数只执行一次”这一知识点,这个知识点正好和
装饰器实现单例模式 相关,所以打算放到那里讲,有兴趣的朋友可以去看。
四、考察
留一个小问题,now()的返回结果中,
为什么是 call now()
而不是 call wrapper()
?
hint:答案就在上面的讲解中。
1 声望
0 粉丝
推荐阅读
捕获异常Exceptions
如果divide函数这样写,会有四个弊端:1)繁琐针对三种返回结果,现在call_divide函数如果调用它,就必须对三种调用结果进行判断,然后处理逻辑。那如果还有call_call_divide函数调用call_divide呢?这意味着后面...
nanakio阅读 729
基于Sanic的微服务基础架构
使用python做web开发面临的一个最大的问题就是性能,在解决C10K问题上显的有点吃力。有些异步框架Tornado、Twisted、Gevent 等就是为了解决性能问题。这些框架在性能上有些提升,但是也出现了各种古怪的问题难以...
jysong赞 6阅读 3.9k评论 3
滚蛋吧,正则表达式!
你是不是也有这样的操作,比如你需要使用「电子邮箱正则表达式」,首先想到的就是直接百度上搜索一个,然后采用 CV 大法神奇地接入到你的代码中?
良许赞 4阅读 2.2k
又一款眼前一亮的Linux终端工具!
今天给大家介绍一款最近发现的功能十分强大,颜值非常高的一款终端工具。这个神器我是在其他公众号文章上看到的,但他们都没把它的强大之处介绍明白,所以我自己体验一波后,再向大家分享自己的体验。
良许赞 5阅读 1.8k
FastAPI性能碾压Flask?
不止一次的听过,FastAPI性能碾压Flask,直追Golang,不过一直没有测试过,今天闲着没事测试一下看看结果。不知道是哪里出了问题,结果大跌眼镜。
二毛erma0赞 2阅读 10.1k评论 3
程序员适合创业吗?
大家好,我是良许。从去年 12 月开始,我已经在视频号、抖音等主流视频平台上连续更新视频到现在,并得到了不错的评价。每个视频都花了很多时间精力用心制作,欢迎大家关注哦~考虑到有些小伙伴没有看过我的视频,...
良许赞 3阅读 1.8k
Python之如何优雅的重试
为了避免偶尔的网络连接失败,需要加上重试机制,那么最简单的形式就是在对应的代码片段加一个循环,循环体里使用异常捕获,连接成功时退出循环,否则就重复执行相关逻辑,此时修改之后的函数f如下
Harpsichord1207赞 3阅读 7.3k
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。