Python——装饰器
这里的装饰器其实就是设计模式中的装饰器模式,有兴趣的话,可以看一下这篇博客:装饰器模式
使用装饰器,一般是在这种情况下使用:
以前定义的一个函数,之前能够完成所需的功能,但是现在发生了新需求,于是之前的函数就不能满足要求了,此时,如果直接修改以前的函数,是一个很不推荐的方法,如果能够通过额外的处理,满足新需求,这才是推荐的。
情景
原需求:
之前有1个函数,都接收一个参数,然后打印出来(这里只是举例子,所以使用一个简单的操作)
def func1(s): print("this is func1", s) func1("hello world") # this is func1 hello world
新需求:
现在需求发生改变了,需要在执行这两个函数时,要打印出执行该函数时的时间戳,有2种方案:
方案1、直接修改func1和func2,在函数内部直接添加一个打印时间戳的语句:
import time def func1(s): print(time.time()) print("this is func1", s) func1("hello world") # 1539527465.7553966 # this is func1 hello world
使用这种方法,可以满足新需求,但是不推荐,因为如果每次都有新需求都去修改以前的代码,那么就说明代码的整体架构不合理。
方案2:加一个外壳(有点装饰器的意思),在调用原函数之前,先进行其他操作
import time def func1(s): print("this is func1", s) # 意思很好理解,就是不直接调用之前的函数,也不修改之前的函数 # 而是在调用原来的函数之前,进行其他操作 def test(func, s): print(time.time()) #先打印时间戳 func(s) test(func1, "hello world") # 1539527924.1535952 # this is func1 hello world
方案分析:
方案1能实现功能,但是已经提到了不推荐。
方案2好像是能实现功能,这里之所以说是好像,这是因为,他等价于在执行函数先执行其他操作,而打印地时间戳,并不是函数执行时的时间戳。类似于下面这个代码意思:
import time def func1(s): print("this is func1", s) # 等价于在电泳函数之前先进性其他操作,但是这个时间戳并不一定是函数开始执行的时间 print(time.time()) test(func1, "hello world") # 1539527924.1535952 # this is func1 hello world
另外,方案2中,定义了一个新的函数,也就是说,要想实现新的功能,那么就必须调用新的函数,只不过将原函数当做参数传入新的函数而已,那么在调用这个函数的地方都要做修改,那还不如直接重新定义一个新的函数呢,然后直接调用新的函数即可,所以,这也是一大问题。
使用装饰器
最简陋的装饰器:
import time def func1(s): print("this is func1", s) # 定义装饰器, 装饰器名称为my_decorator,也可是其他的 def my_decorator(func): # 新增的业务逻辑,都是写在wrapper中 def wrapper(s): print(time.time()) func(s) return wrapper f = my_decorator(func1) f("hello world") # 1539529258.4292638 # this is func1 hello world
上面这个代码,好像和方案2没有什么区别,要说有区别的话,也就是上面这个代码返回的是一个函数,而方案二没有任何返回,是直接在新函数中调用的原函数。
上面这个代码和方案2有一样的问题:调用的时候,不是使用原函数名,虽然在新的函数内调用的是原函数名,但是在外部调用的却是新函数名,这样是很不好的,需要修改其他调用该函数的地方。
python语法糖:
import time # 定义装饰器, 装饰器名称为my_decorator,也可是其他的 def my_decorator(func): # 新增的业务逻辑,都是写在wrapper中 def wrapper(s): print(time.time()) func(s) return wrapper # 在函数名之前加上@装饰器 @my_decorator def func1(s): print("this is func1", s) # 加了装饰器后,可以通过调用原函数就能满足新需求 func1("hello world") # 1539529258.4292638 # this is func1 hello world
这就是python中装饰器的核心,添加新功能后,仍旧可以使用原函数来完成新功能。可以定义时复杂,但不要调用时复杂!
拓展一:使用可变参数
现在有这么一个问题:func1执行的时候,需要1个参数;但是现在有一个func2,需要接收两个参数(甚至有func3,接收3个参数),也需要增加同样的需求,那么,需要再为func2定义一个装饰器吗?如果不再定义一个装饰器,那wrapper里面接收的参数,只是1个参数,那怎么让多个函数都使用一个装饰器呢?答案:在wrapper中使用可变参数。
import time def my_decorator(func): # wrapper中不再接收指定个数的参数,而是接收可变参数,格式为*args,可以更改 def wrapper(*args): print(time.time()) func(*args) return wrapper # 在函数名之前加上@装饰器 @my_decorator def func1(s): print("this is func1", s) @my_decorator def func2(s1, s2): print("this is func2", s1, s2) func1("hello world") # 1539530842.152668 # this is func1 hello world func2("hello python", "zen of python") # 1539530842.152668 # this is func2 hello python zen of python
拓展2:使用关键字参数