python学习:函数---装饰器

周小董 2020-06-10

一、什么是装饰器
  
  现在有一个需求,年终考核的时候需要测试某个函数的执行效率,我们怎么做呢?比如这个函数叫func。
  
  对于绝大多数人来说,第一印象肯定是如下的实现方法:

import time
def func():
    start = time.time()
    time.sleep(1)  # 模拟程序延迟1秒
    print(f‘恭喜发大财!!!‘)
    end = time.time()
    print(end - start)

func()

上述的解决办法是直接在func函数的开始和结尾添加代码来计算这个函数的执行时间。对于这个函数我们已经能计算出其执行时间,需求已经实现了。测试完成之后,我们再删除计算时间的代码就ok了。
  
  但是,如果我们现在还有更多的函数都要去计算其执行时间,我们该怎么办呢?如果还是这样做,我们就需要为了暂时解决这个需求而在每个函数内部添加代码计算执行时间,等到考核结束了我们又要逐个函数去删除增加的代码。效率太低了。
  
  此时,我们可能会想到把计算执行时间的代码封装成一个函数,在函数中调用原来的函数,这样会少些很多代码,而且也不用修改原来函数的代码,经过改造后的代码如下:

import time
def func():
    time.sleep(1)  # 模拟程序延迟1秒
    print(f‘恭喜发大财!!!‘)

def func2():
    time.sleep(1)  # 模拟程序延迟1秒
    print(f‘忽如一夜春风来!!!‘)

def timer(f):
    start = time.time()
    f()
    end = time.time()
    print(end - start)

timer(func)
timer(func2)

上述代码将计算时间的过程封装成函数timer后,只需将原来函数的名称传给timer,调用的地方统一改为调用timer函数就可以了,大大减少了重复代码的编写,一切似乎变得很完美。
  
  但是,冥冥中总觉得有些地方不对劲,到底是哪里有问题呢?哦,原来是要调用func,func2的地方都要改成调用timer,这明显是不合理的,完全是釜底抽薪。现实中我们已经写好了一个函数,不可能说由于你为了满足某个临时的需求,需要将调用这个函数的地方,全部改成调用另外一个函数。这样处理还不如第一种方式。
  
  所以,需要继续改进。之前学闭包的时候,记得有一个闭包的常用方式,如下:

def outer():
    age = 18
    def inner():
        print(age)
    return inner

f = outer()
f()

在这里,return将内部函数名返回给了outer,在调用outer的时候获得了inner的内存地址,就可以直接用这个inner函数的内存地址在外面来调用inner。
  
  如果我也能在内部函数调用原来的func函数,然后内部函数的内存地址返回给outer,这样我就可以在调用outer后把inner的内存地址赋给一个和func相同名称的变量,然后使用func()。这样就不会改变原来的func的调用方式,也不会改变原来func函数的内部代码,两全其美,这就是装饰器,timer就叫装饰器函数。如下:

import time

def func():
    time.sleep(1)  # 模拟程序延迟1秒
    print(f‘恭喜发大财!!!‘)

def timer(f):
    def inner():
        start = time.time()
        f()
        end = time.time()
        print(end - start)
    return inner

func = timer(func)
func()

二、装饰器的作用
  
  在不改变原来函数的源代码以及调用方式的前提下,为函数添加新的功能。
  
  三、原则:开放封闭原则
  
  1、开放:对扩展是开放的
  
  任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能 。
  
  2、封闭:对修改是封闭的
  
  对于已经交付给其他人调用的函数,如果我们修改了这个函数的源代码,就有可能印象其他已经在调用这个函数的用户。

相关推荐