(Python)装饰器

在了解函数的装饰器之前,我们先了解一个开发上的原则:开放封闭原则

    软件上线后,就要遵行这个原则,它就是对修改源代码是封闭的,对功能是开放的。为了遵行这个原则,我们就要找到一种好的解决方案来实现不修改一个功能源代码,以及它的调用方式的前提下,为它加上新的功能。

装饰器就是在不修改被装饰对象源代码与调用方式的前提下,为被装饰对象添加新功能。

    我们来看一小段的代码:

import time
def index(): 
    time.sleep(3)
    print('这是一小段代码')
index()

    现在我们遇到了一个问题。我们要在不修改源代码和调用方式的情况下,添加计算出一下index()这个函数的运行时间。

我们可能马上反应到这样来修改:

import time
def index():
    start_time=time.time()
    time.sleep(1)
    print('这是一小段代码')
    stop_time=time.time()
    print('运行时间是:%s秒' %(stop_time-start_time))

index()

明眼人一看就知道,你这个代码,虽然调用方式没有变,功能也增加了,但是修改入了源代码了呀!所以不行。

后来你又想不如这样修改一下吧:

import time
def index(): 
    time.sleep(3)
    print('这是一小段代码')

start_time = time.time()
index()
stop_time = time.time()
print('运行时间是:%s秒' % (stop_time - start_time))

上面的代码一看,index函数的源代码没有修改,调用方式也不变,也增加了功能,没问题。应该可以了吧。但技术大佬一看,你这个代码写的也太low了吧。如果又要计算其它的功能的运行时间,要不是不断的要复制下面的三句代码:

start_time = time.time()
#index()其它的功能
stop_time = time.time()
print('运行时间是:%s秒' % (stop_time - start_time))

这样不就造成代码冗余了吗。老板一看就知道你的技术太low了,想把你开了。

于是你就想,那就高端一点,我就把它们弄成函数来调用:

import time
def index():
    time.sleep(3)
    print('这是一小段代码')

def wrapper(func):
        start_time= time.time()
        func()
        stop_time=time.time()
        print('运行时间是:%s秒' % (stop_time - start_time))
wrapper(index)
看这代码是高端了一点,但是,你把函数的调用方式给改了。完全违背了开放封闭原则。还是要被老板开除了,于是你就请教技术大佬,大佬说这简单呀,你用闭包函数不就得了麻。这时你就懵逼了,什么是闭包函数呀,你不懂。

    大佬就说,闭包函数就是定义在函数内部的函数,并且该函数包含对外部函数作用域中的名字的引用。你听了还是一脸的懵,大佬于是快速的敲了如下代码让你理解:

def outter():
    name='monicx'
    def inner():
        print('my name is %s' %name)
    return inner
inner=outter()
inner()

从这上面代码当中你看出了一些门道,outter()函数当中嵌套了一个inner()函数,inner()函数就是在outter()函数里面,一般内部函数在外部是不能被调用的,但是你看到了outter()函数体内的最后一句代码把inner()的名称空间返回了。所以当调用outter()的时候,就得到了inner函数的名称空间,于是把它当作一个值,赋值给一个变量名。于是把它赋给inner这个变量名。通过这个变量名加个()就可以调用里面的inner()函数。并且outter()函数作用域内的name变量的值还可以被inner()使用。于是你有点理解闭包函数的强大了。

    于你迫不及待的用刚学到的知识点来优化之前写的low逼代码,得到了下面代码:

import time
def index():
    time.sleep(3)
    print('这是一小段代码')
    

def auth(func):#func=最原始的index
    def wrapper():
            start_time= time.time()
            func()
            stop_time=time.time()
            print('运行时间是:%s秒' % (stop_time - start_time))       
    return wrapper
index=auth(index)#新的index=wrapper

index()#wrapper()

上面的在auth(index)开始执行的时候,就开始执行auth()函数体内的代码,

并把源化码的index函数名称空间当作参数传入。赋值给func

然后在auth()函数体内定义了wrapper()函数,

接着把wrapper()函数的名称空间作为调用auth()函数的返回值。

把这个返回值赋给index变量,此时这个新的index=wrapper

因为wapper是函数名,所以index也是一个函数名。

所以接下通过新的index()就能调用auth()函数内的wrapper()函数的运行。

接着执行到func(),func()就是执行源代码的index()函数。

这么一系列下来,你发现此时的调的index()已经被装饰上了一个计算出一下index()这个函数的运行时间的功能,且没有改变原来index()函数体内的代码!!!感叹这样的装饰实在太棒了。

于是你就迫及待的想用它装饰别的函数。

import time
def index():
    time.sleep(3)
    print('这是一小段代码')

def home(name):
    time.sleep(2)
    print('这是另一个%s的代码' %name)
#==============装饰器===================
def auth(func):  # func=最原始的index
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('运行时间是:%s秒' % (stop_time - start_time))

    return wrapper

index = auth(index)  # 新的index=wrapper
home= auth(home)
#===============end=====================
index()  # wrapper()
home(name='moncix')

运行结果:

Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/untitled/day20/test.py", line 58, in <module>
    home(name='moncix')
TypeError: wrapper() got an unexpected keyword argument 'name'
这是一小段代码
运行时间是:3.000171422958374秒

怎么会出bug呢!!!!哦原来传参出现了错误。index()是无参的函数,而home(name)是有参的,所以你就想到运用可变参数的知识(*:来解决按位置定义的实参,**:来解决按关键字定义的实参)就可以让这个装饰器,不管有参或无参函数都可以装饰了。改进后的代码如下:

import time
def index():
    time.sleep(3)
    print('这是一小段代码')

def home(name):
    time.sleep(2)
    print('这是另一个%s的代码' %name)
#==============装饰器===================
def auth(func):  # func=最原始的index
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        stop_time = time.time()
        print('运行时间是:%s秒' % (stop_time - start_time))

    return wrapper

index = auth(index)  # 新的index=wrapper
home= auth(home)
#===============end=====================
index()  # wrapper()
home(name='moncix')

这样代码就运行成功了!!!!

这是一小段代码
运行时间是:3.000171661376953秒
这是另一个moncix的代码
运行时间是:2.0001144409179688秒

后来你发现装饰器还有专门的语法糖:

    先把装饰函数放在被装饰函数的前面,然后在被装饰函数的前面加上用一行:@装饰函数名,即可。

import time
#==============装饰器===================
def auth(func):  # func=最原始的index
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        stop_time = time.time()
        print('运行时间是:%s秒' % (stop_time - start_time))

    return wrapper
#===============end=====================
@auth #@auth等于index = auth(index)
def index():
    time.sleep(3)
    print('这是一小段代码')

@auth#@auth等于home= auth(home(name))
def home(name):
    time.sleep(2)
    print('这是另一个%s的代码' %name)

index()
home(name='moncix')

运行结果不变,你发现装饰器的语法糖真的太好用了,在用它的时候只要在被装饰函数上面用专门的一行来@它就好了。

后来你写了不少的装饰器,你总结出了一个短小精悍的装饰器的模板供那些小白参考:

不自带参装饰器:

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

自带参装饰器:

def parameters(x, y, z):
    def outter(func):
        def wrapper(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return wrapper
    return outter

猜你喜欢

转载自blog.csdn.net/miaoqinian/article/details/79902257
今日推荐