前言:
Python 装饰器 一节中,我们引入了装饰器的概念,以及常见的4中装饰器模型,本节我们会探索一下装饰器的装饰过程,以及装饰器装饰类的情况,以及装饰器使用的注意事项。
装饰器的装饰过程:
装饰时部分代码会执行
先来一个简单的示例:
def log(func):
print('start to decorate -----------')
def wrapper():
print('log---------------start')
func()
print('log---------------end')
return wrapper
@log
def hh():
print('hhhhhhhh')
print('================')
hh()
输出:
start to decorate -----------
================
log---------------start
hhhhhhhh
log---------------end
如码:使用·log()
来装饰 hh()
从输出我们可以看到,在print('======')
之前还会有一段输出,是在装饰器函数log()
中的一段代码,可见在@log
装饰def hh()
时是会有部分代码执行的。然后当执行hh()
时会有其他代码执行。解释一下过程:
-
@log
即hh = log(hh)
代码从上到下执行print('start to decorate ------')
则控制台输出相应信息 - 然后
def wrapper():
python解释器到这直接跳过, 然后到return wrapper
所以此时,hh = wrapper
-
hh()
即wrapper()
然后执行wrapper()
中的代码
再来看一下带参装饰器的版本即hh() = log(info)(hh)()
:
def log(info):
print('------%s------'% info)
def decorator(func):
print('start to decorate -----------')
def wrapper():
print('log---------------start')
func()
print('log---------------end')
return wrapper
return decorator
@log('im info')
def hh():
print('hhhhhhhh')
print('================')
hh()
输出:
------im info------
start to decorate -----------
================
log---------------start
hhhhhhhh
log---------------end
为什么会在=======
前输出俩行信息呢? 按照上面的思路是因为@log('i'm info')
时 装饰过程执行到了 wrapper()
然后返回了函数。利用我们Python 装饰器 一节中给出的公式 @3.带参装饰器装饰无参函数: hh() = log(text)(hh)()
确实是装饰到return wrapper
.因此会有如上的输出。
嵌套装饰:
先装饰后执行
更进一步理解装饰器的执行过程,上代码:
def log_a(func):
print('---im log_a decorator----------')
def wrapper():
print('----------start----log_a')
func()
print('----------end------log_a')
return wrapper
def log_b(func):
print('---im log_b decorator----------')
def wrapper():
print('----------start----log_b')
func()
print('----------end------log_b')
return wrapper
@log_b
@log_a
def hh():
print('hhhhhhhh')
print('================')
hh()
输出:
---im log_a decorator----------
---im log_b decorator----------
================
----------start----log_b
----------start----log_a
hhhhhhhh
----------end------log_a
----------end------log_b
怎么样是不出乎你的意料了,为了讲的比较清楚此处借助图来理解一下,no picture say a j8 上图:
画的什么鬼~~ 好吧~感觉不是很贴切,看来有待提高,大致解释下:
-
@log_b
@log_a
双重装饰,相当于:hh = log_b( log_a(hh) )
所以装饰的顺序是,先装饰@log_a
后装饰@log_b
因此在装饰的时候会有俩条信息输出,是先输出def log_a()
然后输出def log_b()
- 装饰的结果,相当于图右边的嵌套模型,即执行
hh()
时 从上到下右边的代码块,最外层是log_b()
最内层当然是hh()
,所以有上面的输出结果
小结:
- python装饰时会执行一部分代码,运行时执行一部分代码
- 多重嵌套装饰,先装饰后执行
(log_a 先装饰)
装饰器装饰类:
升麻?装饰器还可以装饰类?没错!装饰器可以装饰类,事实上可以吧类看做特殊的函数,再进一步类也是对象,函数也是对象,在python 中只要这个对象实现了__call__
函数就可以被调用,即可以像函数一样fun()
会执行内部的代码,今天不深入讲解关于__call__
的机制。
上代码(下面是python实现单例模式的一种方法):
def singleton(cls):
__instances = {}
print(cls)
def wrapper(*args,**kwargs):
if cls not in __instances:
__instances[cls] = cls(*args,**kwargs)
return __instances[cls]
return wrapper
@singleton
class A(object):
def hh(self):
print('hhhhhhhh')
print("========")
for i in range(5):
i = A()
print('-------',i)
输出:
<class '__main__.A'>
==========
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>
确实是类也可以被装饰器装饰的,因为类A()
相当于一个函数,A
为函数名,没什么好讲的就是可以,上面的代码适合于单线程的单例模型。要学习更多的关于python 单例的实现方式请转 python 单例模式
装饰器的注意事项:
functools.wraps
函数也是个对象,所以可以将函数赋值给一个变量。然后直接执行该变量就相当于执行了之前的函数
def hh():
print('hhhhhhhh')
a = hh
a()
print(a.__name__)
输出:
hhhhhhhh
hh
如上,函数有一个__name__
属性,是函数名。将函数赋值给变量a
后,a
即指向了hh()
函数,此时执行a()
就相当于执行了hh()
。外推到装饰器中如下:
def log(func):
def wrapper():
func()
return wrapper
@log
def hh():
print('hhhhhh')
hh()
print(hh.__name__)
输出:
hhhhhh
wrapper
此时print('hh.__name__')
输出的是wrapper
。因为此时的hh=wrapper
。这可能会在不知道的地方发生一些不知名的错误,所以我们装饰函数后应该吧,该参数给修改回去。可以显示的修改回去即,在return wrapper
之前 wrapper.__name__ = func.__name__
当然python不会吧这么low的事甩给我们,python总是那么体贴。可以使用Python内置的functools.wraps()
装饰器,来解决该问题,如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper():
func()
#wrapper.__name__ = func.__name__
return wrapper
@log
def hh():
print('hhhhhh')
hh()
print(hh.__name__)
输出:
hhhhhh
hh
可以看到确实是将参数__name__
修改回去了
提前预告:
下一篇:Python 装饰器 应用
主要分析一些装饰器,常用的示例,让我们更加流利的使用装饰器
总结:
python装饰时会执行一部分代码,运行时执行一部分代码
多重嵌套装饰,先装饰后执行 (类似栈结构)
多重装饰:hh = log_b( log_a(hh) )
类也可以被装饰,类相当于一个函数可以被调用
@functools.wraps(func) 修改被装饰函数的__name__参数
声明:
本人也是python 小白,如果上述内容有讲的不对的地方还请各位批评指点。将不胜感激,再次感谢~~~
网友评论