装饰器

1.什么是装饰器

装饰器指的是能为装饰对象添加新功能的工具。

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

2.为什么要使用装饰器

在了解问题之前,我们需要一个原则——开放封闭原则。

开放封闭原则是我们在开发过程中必须要遵守的一个原则。开放封闭指的是对修改封闭,对扩展开放。在以后写代码时,需要对一个函数什么的添加一个

新功能时,切记不能修改它的源代码和它的调用方式。装饰器便是完完全全依照了这个原则,我们添加功能也可以使用装饰器。

既然装饰器遵循开放封闭原则,那么我们便要记住在定义装饰器的时候我们必须遵守装饰器的实现原则:1.不能修改被装饰对象的源代码  2.不能修改被装饰对象的调用方式。

3.装饰器的模板

def outter(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper

下面我就来模拟一下装饰器的由来,从添加功能一片空白,到写一个添加功能的装饰器的过程。

首先我定义一个函数(index函数),模拟一下这个函数的运行时间为1秒

import time
def index():
    time.sleep(1)
    print('from index')

然后我想要为这个函数添加一个计时的功能,当我执行这个函数的时候,我可以返回它的运行时间。

第一个想法便是,直接在这个函数内部修改内容,使它能完成这个需求,代码如下:

def index():
    start_time=time.time()
    time.sleep(1)
    print('from index')
    stop_time=time.time()
    print('time is %s'%(stop_time-start_time))

当我调用index函数时,的确返回了我所需要的函数的运行时间,但是这样写违背了开放封闭原则,我们修改了源代码,这样是不允许的,我想啊,我既然不想修改函数源代码,那么我就在函数外面增加这个功能,于是我在就函数调用时加上这个计时功能,代码如下:

def index():
    time.sleep(1)
    print('from index')

start_time=time.time()
index()
stop_time=time.time()
print('time is %s'%(stop_time-start_time))

嗯,实现了功能,同时没有违背不修改源代码和不修改调用方式的原则,但是这么做如果在程序中调用了很多次index函数,100次的什么的,那么我就要在每一个调用
index时都写上写些代码,程序员写程序最忌讳的就是重复代码,于是这种方式也被我摒弃了.然后,我想既然不想写那么多的代码,那我是不是可以自己写一个函数,
将这些代码写进去,代码如下:

def index():
    time.sleep(1)
    print('from index')
def wrapper():
    start_time=time.time()
    index()
    stop_time=time.time()
    print('time is %s'%(stop_time-start_time))

这时,这个函数的功能是可以实现计时功能的,但是这种方式只能对index这一个函数进行计时功能,对其他的函数不能执行,也就是说这个自定义函数时写死的,于是,我就想着我要把这个函数写活,便联想到了

函数参数的传入方式。我们知道函数体传值的方式有两种。

函数体传值的方式:1.直接以参数传进去 2.以闭包函数形式

首先,我用第一种方式直接以参数传进去,代码如下:

def index():
    time.sleep(1)
    print('from index')
def wrapper(func):
    start_time=time.time()
    func()
    stop_time=time.time()
    print('time is %s'%(stop_time-start_time))
wrapper(index)

发现,函数的调用的方式发生了变化,我们调用wrapper()后才能调用func函数,因此这种方式违反了原则,于是我们写第二种的传值方式,代码如下:

def index():
    time.sleep(1)
    print('from index')


def outter(func):
    def wrapper():
        start_time = time.time()
        func()  # index的原始的内存地址
        stop_time = time.time()
        print('time is %s' % (stop_time - start_time))

    return wrapper
index = outter(index)  # index=outter(index的原始的内存地址),在这里index=wrapper(表示的是将waapper的内存地址赋值给index,也就是 index=waapper的内存地址)
index()

在这里有一个小技巧,我们调用otter()的时候,返回的是一个wrapper的内存地址,我们如果把它赋值一个f,那么其实在使用这种方式传值时也会改变调用方式,那么我们可以
将我们定义原来的调用方式中的函数名index,将wrapper的内存地址赋值给我们新定义的index,那么在后面调用时我们还是调用了index,其实就是给使用者一个错觉,我们的
调用方式没有发生改变,这样也不会使后面其他调用index的操作需要发生变化。

上面这种方式在遵循开放封闭原则的情况下,完成了我们的需求,且方法十分巧妙。

4.装饰器语法糖以及他的代码完美化

参考第三点装饰器的模板以及装饰器的语法糖,我们将上述代码完美化,如下:

def outter(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res=func(*args,**kwargs)  # index的原始的内存地址
        stop_time = time.time()
        print('time is %s' % (stop_time - start_time))
        return res  #这个return是为了func函数中如果有返回值那么我这个功装饰器也要有返回值,不然就没有做到与被装饰对象一致

    return wrapper

@outter   #这一行代码就是index=outter(index)这一行代码,他自己执行了
def index():
    time.sleep(1)
    print('from index')
    return 1

@outter   #这一行代码就是auth=outter(auth)这一行代码,他自己执行了
def auth(name):
    time.sleep(1)
    print('from auth %s'%name)

index()
auth('egon')

杜绝秃头!!!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注