python理解装饰器
一、装饰器是干嘛用的?
-
装饰嘛,就像人化妆一样。在不影响人体功能的前提下,点缀点缀,让你仿佛换个人。
-
-
差不多就是这区别。
二、python中如何实现?
-
使用 @xxx 的方法来实现。
@xxx 其实会做两件事:
- 执行xxx函数。
- xxx函数的返回值,是一个函数对象,会替换掉要装饰的函数。
-
比如这段代码:
def print_a(func): print("A"); return func; @print_a def print_x(): print("X"); print("<------------------>"); print_x();
-
这一段代码做了什么事情?
-
1:
def print_a(func): print("A"); return func;
就是定义了一个print_a 函数,传一个参数。
-
2:
@print_a def print_x(): print("X");
定义了一个名叫print_x()的函数。尝试使用print_a进行装饰。
@print_a 做了什么呢?
它先把print_a函数执行一下。然后print_a函数返回了一个 函数对象。重点:print_a返回的函数对象,会代替真正的print_x(); 当你调用print_x的时候,其实调用的是print_a返回的函数对象 。
-
3:
使用@print_a这种写法,默认print_a会被传一个参数,参数是print_x的函数对象。
-
4:
执行结果是:
/*一开始就先执行了@print_a的函数*/ A /*然后执行了分隔符打印*/ <------------------> /*最后这里执行print_x()函数。这里注意,print_x函数其实是print_a返回的函数对象*/ X 请按任意键继续. . .
-
三、一个简单的装饰器
-
了解了@print_a是怎么工作的。现在看一个简单的装饰器。
def print_world(func): print("world is %s" %func); def print_hello(func): print(func); return print_world; @print_hello def print_string(str): print("传入的参数是:%s" %str); print_string('123');
分析:
这里的@print_hello,print_hello函数会返回一个print_world 的函数对象,然后替换掉print_string,所以,当你调用print_string的时候,其实执行的是print_world.
所以print_string函数被装饰成了print_world。
运行结果:
可以看到,虽然我调用的是print_string(‘123’);但其实调用的是print_world。
-
上面的写法是不好的,因为print_world完全和print_string没有关系。已经是换人了,不是化妆了。
所以换一个写法:
def print_hello(func): def wrapper(*args, **kwargs): print('Hello'); return func(*args, **kwargs); return wrapper; @print_hello def print_string(str): print("传入的参数是:%s" %str); print_string('123');
使用这种写法,就是把print_string函数换成wrapper函数了。wrapper函数先执行了一个print(‘hello’),然后执行func.也就是print_string本来的功能。
这就仿佛是在print_string 函数 每次执行之前,先执行一下其它的东西,也就是print_string被装饰了。
运行结果如下:
你去打印一下print_string的名字,发现它的名字都叫wrapper了,也就是现在print_string已经不是它自己了。
如果要保留print_string自己的名字信息,怎么做呢?
这样写:
from functools import wraps def print_world(func): print("world is %s" %func); def print_hello(func): @wraps(func) def wrapper(*args, **kwargs): print('Hello'); return func(*args, **kwargs); return wrapper; @print_hello def print_string(str): print("传入的参数是:%s" %str); print(print_string.__name__);
运行结果:
使用这个wraps工具,在print_hello替换函数之前用@wraps(func)再装饰一下,动动脚指头想想,都知道这个是改函数信息的。