有时候我们项目中的某些功能做些修改即需要对内部的某些函数添加一些附加功能,但是为了安全起见不想改变函数的源代码以及函数的调用方式,那么装饰器在这个地方会给我们带来很大的帮助。
装饰器(Decorator):(又叫语法糖)
定义:本质是函数,功能(装饰其它函数)就是为其他函数添加附加功能
原则:(1).不能修改被装饰的函数的源代码
(2).不能修改被装饰的函数的调用方式
1.先来实现一个简单的装饰器示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#定义一个简单的装饰器
def simple_wrapper(func):
def wrapper():
print("我是装饰器,我用来装饰%s" % func.__name__)
func()
return wrapper
#需要装饰的函数
@simple_wrapper
def say_hello():
print("Hello World")
#执行say_hello()函数
say_hello()
'''运行结果如下:
我是装饰器,我用来装饰say_hello
Hello World
'''
上边实现了一个简单的装饰器,能过用来装饰不带参数的函数,通过这个简单的示例,我们大概对装饰器的基本实现有一个大概的了解。但是如果想充分了解并掌握装饰的原理必要还要对python中的高阶函数、嵌套函数、以及函数即“变量”等概念有一定的了解和掌握。下边我会对装饰器以及相关的内容进行举例说明。
2.上边实现了一个简单的能够装饰不带参数的装饰器,但在正常情况下,我们的函数都需要传入适当的参数,如何能够实现对有参数的方法进行装饰呢?
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def simple_wrapper(func):
def wrapper(*args, **kwargs):
print("我是装饰器,我用来装饰%s方法" % func.__name__)
func(*args, **kwargs)
return wrapper
@simple_wrapper
def say_hello(name):
print("Hello",name)
#执行say_hello()函数
say_hello("Jack")
'''运行结果:
我是装饰器,我用来装饰say_hello方法
Hello Jack
'''
#是不是同样很简单呢? 这种方式既可以装饰带任意参数的函数,也可以装饰不带参数的函数。
3.上边的装饰器已经可以实现基本要求,即可以完成对指定的函数添加附加功能的作用,但是在某些特定时候,我们需要装饰器自身也带上参数,如何实现呢?
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def my_wrapper(args):
print("我的参数是:",args)
def simple_wrapper(func):
def wrapper(*args, **kwargs):
print("我是装饰器,我用来装饰%s方法" % func.__name__)
func(*args, **kwargs)
return wrapper
return simple_wrapper
@my_wrapper("simple")
def say_hello(name):
print("Hello",name)
#执行say_hello()函数
say_hello("Jack")
'''运行结果:
我的参数是: simple
我是装饰器,我用来装饰say_hello方法
Hello Jack
'''
是不是同样的很简单呢?
简单是简单,但是关键装饰器是怎么实现对其它函数添加附加功能呢?上边的函数是如何运行的呢?下面请看下边的简单示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def simple_wrapper(func):
def wrapper(*args, **kwargs):
print("我是装饰器,我用来装饰%s方法" % func.__name__)
func(*args, **kwargs)
return wrapper
def say_hello(name):
print("Hello",name)
say_hello = simple_wrapper(say_hello) #这里就等同于@simple_wrapper的作用
#其实这里相当于say_hello=wrapper
say_hello("Jack") #所有在执行say_hello("Jack"),就相当调用了wrapper("Jack")函数
简单解释:可以看到上边也能实现了装饰器的功能,对该示例简要分析:因为在python中函数作为一个对象但也可以看成一种“变量”,不仅可以将函数名拿来赋值给其它变量,也可以将函数名当做参数传递给其它函数,并且还可以将函数名作为返回值。(通俗点可以这样说,就是函数名在内存中就是一个内存地址,它指向函数体的内存地址空间,所以可以将函数名当做一个“变量”来进行相关操作,单当在函数名"变量"后边加上()的时候它就变成了函数调用,会去执行函数体。)在看上边的小例子,当执行say_hello = simple_wrapper(say_hello) 这一步的时候,将函数名say_hello当做参数传给了函数simple_wrapper(),但是接着函数simple_wrapper()将内部函数wrapper作为返回值返回,然后say_hello“被重新复制”,即say_hello指向了wrapper函数体的内存空间,接着当执行say_hello("Jack")的时候就相当于执行wrapper("Jack")。
4.通过上边的示例我们会发现当使用装饰器的时候,函数say_hello其实被simple_wrapper取代了,理所当然它的__name__等信息都变成了simple_wrapper函数的信息。如何既能让函数被装饰,又不改变其自身的信息呢?其实在Python里提供了一个functools.wraps,而wraps也是一个装饰器,它作用就是实现这种功能的。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
def simple_wrapper(func):
def wrapper(*args, **kwargs):
print("我是装饰器,我用来装饰%s方法" % func.__name__)
func(*args, **kwargs)
return wrapper
@simple_wrapper
def say_hello(name):
print("Hello",name)
#输出say_hello现在的__name___属性
print(say_hello.__name__) #输出结果为:wrapper
'''
######使用functools.wraps
from functools import wraps
def simple_wrapper(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("我是装饰器,我用来装饰%s方法" % func.__name__)
func(*args, **kwargs)
return wrapper
@simple_wrapper
def say_hello(name):
print("Hello",name)
#输出say_hello现在的__name__属性
print(say_hello.__name__) #输出结果为:say_hello
5.其实一个函数可以同时定义多个装饰器,当一个函数定义多个装饰器时,装饰器的执行顺序是先执行最里层的装饰器最后调用最外层的装饰器,简单示例:
@a
@b
@c
def say_hello():
pass
#该示例中装饰器的执行顺序是c > b > c(等效于a(b(c(say_hello))))