装饰器之基本

匿名 (未验证) 提交于 2019-12-02 23:47:01

为什么要有装饰器?

在学习装饰器之前,一定要了解一个开放封闭原则。软件开发都应该遵循开放封闭原则。

开放封闭原则

  • 对扩展是开放的
  • 对修改是封闭的
为什么说要对扩展是开放的呢?  因为软件开发过程不可能一次性把所有的功能都考虑完全,肯定会有不同的新功能需要不停添加。也就是说需要我们不断地去扩展已经存在代码的功能,这是非常正常的情况。  那为什么说对修改是封闭的呢?  比如说已经上线运行的源代码,比如某个函数内部的代码是不建议直接修改的。因为函数的使用分为两个阶段:函数的定义阶段和函数的调用阶段。因为你不确定这个函数究竟在什么地方被调用了,你如果粗暴的修改了函数内部的源代码,对整个程序造成的影响是不可控的。  总结一下就是:不修改源代码,不修改调用方式,同时还要加上新功能。

什么是装饰器?

装饰器的本质

装饰器本质上可以是任意可调用对象,被装饰的对象也可以是任意可调用对象。

装饰器的功能

在不修改被装饰对象源代码以及调用方式的前提下为它添加新功能。

首先我们先来个例子:

import time import random  def index():     time.sleep(random.randrange(1, 5))  # 随机sleep几秒     print("欢迎访问首页。")      index()

现在需求来了,我需要统计下 index 函数执行耗费的时间。

你可能会很快写出下面的代码:

import time import random  def index():     start_time = time.time()      time.sleep(random.randrange(1, 5))  # 随机sleep几秒     print("欢迎访问首页。")      stop_time = time.time()     print("耗时{}秒。".format(stop_time - start_time))      index() 输出:  欢迎访问首页。 耗时2.0009069442749023秒。

需求是实现了,但是修改了原来 index 函数的源代码。

那么我们就要想另外的办法去实现了。

我们不能修改函数的源代码,那么我们现在能想到的就是定义一个新的函数来实现这个功能。

类似于这样:

def wrapper():     start_time = time.time()     index()  # 在这个函数内部调用下index     stop_time = time.time()     print("耗时{}秒。".format(stop_time - start_time))

这里会有一个问题是,index()写死在wrapper()里了,让我们先优化一下。通过传参数的方式来实现:

def wrapper(func):  # 设置一个参数     start_time = time.time()     func()  # 在这个函数内部调用下func     stop_time = time.time()     print("耗时{}秒。".format(stop_time - start_time))     

这样貌似就可以了,在调用index的地方直接写wrapper(index)就可以了。

但是,这样做会修改原函数的调用方式,原来是index(),现在变成了wrapper(index),显然这么做同样还是不能满足我们的要求。

怎么办呢,我们其实可以将 wrapper 函数改名为 index,这样就实现了既不修改原来的调用方式又扩展了新功能。简直完美。

让我们来写一下:

def wrapper(func):  # 设置一个参数     start_time = time.time()     func()  # 在这个函数内部调用下func     stop_time = time.time()     print("耗时{}秒。".format(stop_time - start_time))      index = wrapper index()  # 报错了。wrapper函数的func参数怎么办?

我们现在的问题是:

我通过修改函数名之后,原函数本身没有参数,但是我定义的新函数内部又用到外面的变量怎么办?

用闭包啊!把这个变量做成外部函数的局部变量:

def timer():     func = index  # 在外部函数定义一个局部变量     def wrapper():  # 设置一个参数         start_time = time.time()         func()  # 在这个函数内部调用下func         stop_time = time.time()         print("耗时{}秒。".format(stop_time - start_time))     return wrapper

上面的写法写死了func = index,我们还可以优化下:

def timer(func):  # 传一个func参数和下面func = index一样都是在my_index函数内部定义了一个局部变量     # func = index  # 在外部函数定义一个局部变量     def wrapper():  # 设置一个参数         start_time = time.time()         func()  # 在这个函数内部调用下func         stop_time = time.time()         print("耗时{}秒。".format(stop_time - start_time))     return wrapper

好了,我们刚刚就已经写好了一个装饰器。

验证一下,我们的成果:

index = timer(index) index() 

输出:

欢迎访问首页。 耗时1.0029211044311523秒。 
  1. 没有修改原函数的源代码。
  2. 没有修改原函数的调用方式。(虽然此时的index已不再是原来的index,但是并没有修改调用index的方式。)

现在我们来验证下我们的成果:

import time import random  def index():     time.sleep(random.randrange(1, 5))  # 随机sleep几秒     print("欢迎访问首页。")  def home():     time.sleep(random.randrange(1, 5))  # 随机sleep几秒     print("欢迎访问个人主页。")  def timer(func):  # 传一个func参数和下面func = index一样都是在my_index函数内部定义了一个局部变量     def wrapper():  # 设置一个参数         start_time = time.time()         func()  # 在这个函数内部调用下func         stop_time = time.time()         print("耗时{}秒。".format(stop_time - start_time))     return wrapper  index = timer(index) home = timer(home) index() home() 输出:  欢迎访问首页。 耗时3.0008788108825684秒。 欢迎访问个人主页。 耗时3.00280499458313秒。

简直完美,功能实现可以装饰任意函数。就是写起来有点麻烦。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!